From 28a2f8c3e2093404f844a2742891252920b19089 Mon Sep 17 00:00:00 2001 From: draedful Date: Wed, 1 Jul 2026 17:28:42 +0300 Subject: [PATCH 1/2] fix(connections): guard isPointInStroke before graph initialization Prevent TypeError when mouse moves before connection Path2D is ready by validating path/context in isPointInStroke and generating path on geometry update. Co-authored-by: Cursor --- .../canvas/connections/BlockConnection.ts | 3 +- .../canvas/connections/bezierHelpers.test.ts | 29 +++++++++++++++++++ .../canvas/connections/bezierHelpers.ts | 12 +++++++- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/components/canvas/connections/bezierHelpers.test.ts diff --git a/src/components/canvas/connections/BlockConnection.ts b/src/components/canvas/connections/BlockConnection.ts index 581d6f7b..7390ee05 100644 --- a/src/components/canvas/connections/BlockConnection.ts +++ b/src/components/canvas/connections/BlockConnection.ts @@ -33,7 +33,7 @@ export class BlockConnection { public readonly cursor = "pointer"; - protected path2d: Path2D; + protected path2d: Path2D = new Path2D(); protected labelGeometry: { x: number; y: number; width: number; height: number } | undefined = undefined; @@ -238,6 +238,7 @@ export class BlockConnection this.geometry.x2 = this.connectionPoints[1].x; this.geometry.y2 = this.connectionPoints[1].y; + this.generatePath(); this.applyShape(); } diff --git a/src/components/canvas/connections/bezierHelpers.test.ts b/src/components/canvas/connections/bezierHelpers.test.ts new file mode 100644 index 00000000..a65dfc73 --- /dev/null +++ b/src/components/canvas/connections/bezierHelpers.test.ts @@ -0,0 +1,29 @@ +import { isPointInStroke } from "./bezierHelpers"; + +describe("isPointInStroke", () => { + it("returns false when path is undefined", () => { + const ctx = document.createElement("canvas").getContext("2d"); + + expect(isPointInStroke(ctx, undefined, 0, 0)).toBe(false); + }); + + it("returns false when path is not a Path2D instance", () => { + const ctx = document.createElement("canvas").getContext("2d"); + + expect(isPointInStroke(ctx, {} as Path2D, 0, 0)).toBe(false); + }); + + it("returns false when context is missing", () => { + const path = new Path2D(); + + expect(isPointInStroke(null, path, 0, 0)).toBe(false); + }); + + it("does not throw when path is invalid", () => { + const ctx = document.createElement("canvas").getContext("2d"); + + expect(() => isPointInStroke(ctx, undefined, 0, 0)).not.toThrow(); + expect(() => isPointInStroke(ctx, {} as Path2D, 0, 0)).not.toThrow(); + expect(() => isPointInStroke(null, new Path2D(), 0, 0)).not.toThrow(); + }); +}); diff --git a/src/components/canvas/connections/bezierHelpers.ts b/src/components/canvas/connections/bezierHelpers.ts index c0e9ee2f..ab08a04f 100644 --- a/src/components/canvas/connections/bezierHelpers.ts +++ b/src/components/canvas/connections/bezierHelpers.ts @@ -57,7 +57,17 @@ export function getPointOfBezierCurve( /* eslint-enable no-restricted-properties */ } -export function isPointInStroke(ctx: CanvasRenderingContext2D, path: Path2D, x: number, y: number, threshold?: number) { +export function isPointInStroke( + ctx: CanvasRenderingContext2D | undefined | null, + path: Path2D | undefined | null, + x: number, + y: number, + threshold?: number, +): boolean { + if (!ctx || !path || !(path instanceof Path2D)) { + return false; + } + const l = ctx.lineWidth; if (threshold) { ctx.lineWidth = threshold; From dca32fdb4b078430351ab726453d0ccf0b75012d Mon Sep 17 00:00:00 2001 From: draedful Date: Wed, 1 Jul 2026 18:49:47 +0300 Subject: [PATCH 2/2] fix(connections): fix prettier trailing comma in isPointInStroke Co-authored-by: Cursor --- src/components/canvas/connections/bezierHelpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/canvas/connections/bezierHelpers.ts b/src/components/canvas/connections/bezierHelpers.ts index ab08a04f..4aa6014e 100644 --- a/src/components/canvas/connections/bezierHelpers.ts +++ b/src/components/canvas/connections/bezierHelpers.ts @@ -62,7 +62,7 @@ export function isPointInStroke( path: Path2D | undefined | null, x: number, y: number, - threshold?: number, + threshold?: number ): boolean { if (!ctx || !path || !(path instanceof Path2D)) { return false;