fix: Arrow point index Out-of-Bounds (#10922)

* fix: Make OOB not fatal

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: More conservative temp arrow state update

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* chore: Capture condition variables in binding restoration failure

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

---------

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
Márk Tolmács
2026-03-21 19:26:47 +01:00
committed by GitHub
parent 81ab857a6f
commit 987173b52f
3 changed files with 30 additions and 26 deletions
+10 -4
View File
@@ -476,10 +476,12 @@ export class LinearElementEditor {
}); });
} }
invariant( if (
lastClickedPoint > -1 && lastClickedPoint < 0 ||
selectedPointsIndices.includes(lastClickedPoint) && !selectedPointsIndices.includes(lastClickedPoint) ||
element.points[lastClickedPoint], !element.points[lastClickedPoint]
) {
console.error(
`There must be a valid lastClickedPoint in order to drag it. selectedPointsIndices(${JSON.stringify( `There must be a valid lastClickedPoint in order to drag it. selectedPointsIndices(${JSON.stringify(
selectedPointsIndices, selectedPointsIndices,
)}) points(0..${ )}) points(0..${
@@ -487,6 +489,10 @@ export class LinearElementEditor {
}) lastClickedPoint(${lastClickedPoint})`, }) lastClickedPoint(${lastClickedPoint})`,
); );
// Fall back to the actual last point as a last resort.
lastClickedPoint = element.points.length - 1;
}
// point that's being dragged (out of all selected points) // point that's being dragged (out of all selected points)
const draggingPoint = element.points[lastClickedPoint]; const draggingPoint = element.points[lastClickedPoint];
// The adjacent point to the one dragged point // The adjacent point to the one dragged point
+8 -12
View File
@@ -6995,27 +6995,23 @@ class App extends React.Component<AppProps, AppState> {
}, },
{ informMutation: false, isDragging: false }, { informMutation: false, isDragging: false },
); );
const newLastIdx = multiElement.points.length - 1;
this.setState({ this.setState({
selectedLinearElement: { selectedLinearElement: {
...selectedLinearElement, ...selectedLinearElement,
selectedPointsIndices: selectedPointsIndices: selectedLinearElement.selectedPointsIndices
selectedLinearElement.selectedPointsIndices?.includes(
multiElement.points.length,
)
? [ ? [
...selectedLinearElement.selectedPointsIndices.filter( ...new Set(
(idx) => selectedLinearElement.selectedPointsIndices.map((idx) =>
idx !== multiElement.points.length && Math.min(idx, newLastIdx),
idx !== multiElement.points.length - 1, ),
), ),
multiElement.points.length - 1,
] ]
: selectedLinearElement.selectedPointsIndices, : selectedLinearElement.selectedPointsIndices,
lastCommittedPoint: lastCommittedPoint: multiElement.points[newLastIdx],
multiElement.points[multiElement.points.length - 1],
initialState: { initialState: {
...selectedLinearElement.initialState, ...selectedLinearElement.initialState,
lastClickedPoint: multiElement.points.length - 1, lastClickedPoint: newLastIdx,
}, },
}, },
}); });
+3 -1
View File
@@ -251,7 +251,9 @@ const repairBinding = <T extends ExcalidrawArrowElement>(
}; };
} }
console.error(`could not repair binding for element`); console.error(
`Could not repair binding for element "${boundElement?.id}" out of (${elementsMap?.size}) elements`,
);
} catch (error) { } catch (error) {
console.error("Error repairing binding:", error); console.error("Error repairing binding:", error);
} }