Editor has no Bezier-handle numeric input — the right panel shows node X/Y but the user can only edit the controlIn/controlOut handles by dragging them in the canvas, with no numeric precision #211

Closed
opened 2026-10-04 00:02:06 +00:00 by Mark · 1 comment
Owner

Finding

src/components/editor/EditorRightPanel.tsx's "Node Edit" section shows:

  • X coordinate (<NodeCoordInput axis="x" ... />)
  • Y coordinate (<NodeCoordInput axis="y" ... />)
  • Node type (corner / smooth / symmetric)

It does NOT show controlIn or controlOut (the Bezier handle coordinates for smooth/symmetric nodes).

grep -n 'controlIn\|controlOut' src/components/editor/EditorRightPanel.tsx returns 0 matches. The control points are drawn on the canvas (canvasRenderer.ts:75-76 and :155, :185, :206) and can be dragged via the canvas, but the right panel has no numeric input for them.

A user with a smooth node at (100, 100) with controlIn at (95, 95) wants to nudge the handle to (95, 100) (to make the curve flow more horizontally):

  • They have to find the small draggable handle in the canvas (5px handle on a zoomed-out view is hard to grab)
  • Drag with the mouse (no precision; releases at 1px granularity)
  • If they overshoot, they have to drag back

A numeric input <NodeCoordInput axis="ci.x" ... /> with the same step + commit pattern as X/Y would let them type the exact value.

Where

  • Node edit section: src/components/editor/EditorRightPanel.tsx:255-280 (X, Y, type)
  • No control point input: grep -n 'controlIn' src/components/editor/EditorRightPanel.tsx returns 0
  • Control points: src/components/editor/types.ts:10 (controlIn?: Point, controlOut?: Point)
  • Canvas dragging: src/components/editor/canvasRenderer.ts:278-282 (draws handles)
  • Drag handlers: presumably exist; let me verify by searching for onControlDrag or similar

Suggested fix shape

For nodes where type === 'smooth' || type === 'symmetric', show two more rows in the right panel:

{firstSelectedNode.type !== 'corner' && firstSelectedNode.controlIn && (
  <>
    <NodeCoordInput
      axis="ci.x"
      nodeId={firstSelectedNode.id}
      value={firstSelectedNode.controlIn.x}
      onCommit={(nodeId, x) => onUpdateControlPoint(nodeId, 'in', x, firstSelectedNode.controlIn.y)}
    />
    <NodeCoordInput
      axis="ci.y"
      nodeId={firstSelectedNode.id}
      value={firstSelectedNode.controlIn.y}
      onCommit={(nodeId, y) => onUpdateControlPoint(nodeId, 'in', firstSelectedNode.controlIn.x, y)}
    />
    {/* same for controlOut */}
  </>
)}

onUpdateControlPoint (a new prop on the right panel) dispatches an action that updates the node's controlIn or controlOut and pushes history.

The symmetric case needs care: for a symmetric node, editing controlIn should mirror the change to controlOut (and vice versa). The canvas's Bezier-handle dragging already does this; the right panel should too.

Severity

Low. Users with smooth/symmetric nodes want precision; the canvas drag works for most cases. Power users notice.

Discovered by

Gap analysis (2026-10-02) of editor node editing.

  • #170 (closed) — Editor snap toggles do something. Snap is also relevant to handle positioning.
  • The X/Y NodeCoordInput is the pattern to mirror; the helper is local to the file.
## Finding `src/components/editor/EditorRightPanel.tsx`'s "Node Edit" section shows: - X coordinate (`<NodeCoordInput axis="x" ... />`) - Y coordinate (`<NodeCoordInput axis="y" ... />`) - Node type (corner / smooth / symmetric) It does NOT show `controlIn` or `controlOut` (the Bezier handle coordinates for smooth/symmetric nodes). `grep -n 'controlIn\|controlOut' src/components/editor/EditorRightPanel.tsx` returns 0 matches. The control points are drawn on the canvas (`canvasRenderer.ts:75-76` and `:155, :185, :206`) and can be dragged via the canvas, but the right panel has no numeric input for them. A user with a smooth node at (100, 100) with `controlIn` at (95, 95) wants to nudge the handle to (95, 100) (to make the curve flow more horizontally): - They have to find the small draggable handle in the canvas (5px handle on a zoomed-out view is hard to grab) - Drag with the mouse (no precision; releases at 1px granularity) - If they overshoot, they have to drag back A numeric input `<NodeCoordInput axis="ci.x" ... />` with the same step + commit pattern as X/Y would let them type the exact value. ## Where - Node edit section: `src/components/editor/EditorRightPanel.tsx:255-280` (X, Y, type) - No control point input: `grep -n 'controlIn' src/components/editor/EditorRightPanel.tsx` returns 0 - Control points: `src/components/editor/types.ts:10` (`controlIn?: Point`, `controlOut?: Point`) - Canvas dragging: `src/components/editor/canvasRenderer.ts:278-282` (draws handles) - Drag handlers: presumably exist; let me verify by searching for `onControlDrag` or similar ## Suggested fix shape For nodes where `type === 'smooth' || type === 'symmetric'`, show two more rows in the right panel: ```tsx {firstSelectedNode.type !== 'corner' && firstSelectedNode.controlIn && ( <> <NodeCoordInput axis="ci.x" nodeId={firstSelectedNode.id} value={firstSelectedNode.controlIn.x} onCommit={(nodeId, x) => onUpdateControlPoint(nodeId, 'in', x, firstSelectedNode.controlIn.y)} /> <NodeCoordInput axis="ci.y" nodeId={firstSelectedNode.id} value={firstSelectedNode.controlIn.y} onCommit={(nodeId, y) => onUpdateControlPoint(nodeId, 'in', firstSelectedNode.controlIn.x, y)} /> {/* same for controlOut */} </> )} ``` `onUpdateControlPoint` (a new prop on the right panel) dispatches an action that updates the node's `controlIn` or `controlOut` and pushes history. The symmetric case needs care: for a symmetric node, editing controlIn should mirror the change to controlOut (and vice versa). The canvas's Bezier-handle dragging already does this; the right panel should too. ## Severity Low. Users with smooth/symmetric nodes want precision; the canvas drag works for most cases. Power users notice. ## Discovered by Gap analysis (2026-10-02) of editor node editing. ## Related - #170 (closed) — Editor snap toggles do something. Snap is also relevant to handle positioning. - The X/Y `NodeCoordInput` is the pattern to mirror; the helper is local to the file.
Author
Owner

Shipped via commit 878e8ab on origin/main. The right panel now renders two extra 2-col grids (CI.X / CI.Y for controlIn, CO.X / CO.Y for controlOut) under the existing X/Y row whenever a smooth or symmetric node is selected. Corner nodes keep the original X/Y-only UI.

For symmetric nodes, editing one handle mirrors the change onto the other (anti-symmetric reflection around the anchor) so the bezier stays well-formed. Smooth nodes only touch the requested handle.

onUpdateControlPoint is optional — older callers without it simply don't render the handle rows, so no migration is required to keep the panel working.

1031/1032 tests pass; tsc / lint / build clean.

Shipped via commit 878e8ab on `origin/main`. The right panel now renders two extra 2-col grids (CI.X / CI.Y for controlIn, CO.X / CO.Y for controlOut) under the existing X/Y row whenever a smooth or symmetric node is selected. Corner nodes keep the original X/Y-only UI. For symmetric nodes, editing one handle mirrors the change onto the other (anti-symmetric reflection around the anchor) so the bezier stays well-formed. Smooth nodes only touch the requested handle. `onUpdateControlPoint` is optional — older callers without it simply don't render the handle rows, so no migration is required to keep the panel working. 1031/1032 tests pass; tsc / lint / build clean.
Mark closed this issue 2026-10-04 03:46:00 +00:00
Sign in to join this conversation.
No description provided.