Skip to content

Commit 86dcc4a

Browse files
committed
fix(canvas): guard reflow commit undo-resume with try/finally
A throw from updateObject during the text/shape reflow commit could leave undo recording paused; mirror the glyph block's try/finally.
1 parent 5b0ea89 commit 86dcc4a

1 file changed

Lines changed: 55 additions & 49 deletions

File tree

‎src/components/Canvas/hooks/useKonvaTransformer.ts‎

Lines changed: 55 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -811,34 +811,37 @@ export function useKonvaTransformer({
811811
lrNode?.scaleY(1);
812812
const cur = id ? findObjectById(getCurrentObjects(), id) : undefined;
813813
const temporal = useLabelStore.temporal.getState();
814-
if (lr.changed && id && cur && !isGroup(cur)) {
815-
const cp = cur.props as { blockWidth?: number; blockLines?: number; blockHeight?: number };
816-
// ^TB collapses width + clip height; ^FB width + line count.
817-
const secondAxis = lr.mode === "tb"
818-
? { blockHeight: cp.blockHeight }
819-
: { blockLines: cp.blockLines };
820-
const snapAxis = lr.mode === "tb"
821-
? { blockHeight: lr.snapshot.blockHeight }
822-
: { blockLines: lr.snapshot.blockLines };
823-
const final = {
824-
x: cur.x,
825-
y: cur.y,
826-
props: { blockWidth: cp.blockWidth, ...secondAxis },
827-
};
828-
updateObject(id, {
829-
x: lr.snapshot.x,
830-
y: lr.snapshot.y,
831-
props: {
832-
blockWidth: lr.snapshot.blockWidth,
833-
...snapAxis,
834-
},
835-
});
836-
temporal.resume();
837-
updateObject(id, final);
838-
} else {
814+
// try/finally so a throw can't leave undo paused (resume is idempotent).
815+
try {
816+
if (lr.changed && id && cur && !isGroup(cur)) {
817+
const cp = cur.props as { blockWidth?: number; blockLines?: number; blockHeight?: number };
818+
// ^TB collapses width + clip height; ^FB width + line count.
819+
const secondAxis = lr.mode === "tb"
820+
? { blockHeight: cp.blockHeight }
821+
: { blockLines: cp.blockLines };
822+
const snapAxis = lr.mode === "tb"
823+
? { blockHeight: lr.snapshot.blockHeight }
824+
: { blockLines: lr.snapshot.blockLines };
825+
const final = {
826+
x: cur.x,
827+
y: cur.y,
828+
props: { blockWidth: cp.blockWidth, ...secondAxis },
829+
};
830+
updateObject(id, {
831+
x: lr.snapshot.x,
832+
y: lr.snapshot.y,
833+
props: {
834+
blockWidth: lr.snapshot.blockWidth,
835+
...snapAxis,
836+
},
837+
});
838+
temporal.resume();
839+
updateObject(id, final);
840+
}
841+
} finally {
839842
temporal.resume();
843+
cleanupTransformState();
840844
}
841-
cleanupTransformState();
842845
return;
843846
}
844847
// Shape live reflow: same collapse-to-one-entry as the text block. Dims were
@@ -851,31 +854,34 @@ export function useKonvaTransformer({
851854
srNode?.scaleY(1);
852855
const cur = id ? findObjectById(getCurrentObjects(), id) : undefined;
853856
const temporal = useLabelStore.temporal.getState();
854-
if (sr.changed && id && cur && !isGroup(cur)) {
855-
const sp = cur.props as { width: number; height: number };
856-
// Same snap/round/clamp contract as commitWidthHeightTransform, applied
857-
// to the already-baked float dims.
858-
const width = Math.max(1, snap(Math.round(sp.width)));
859-
const height = Math.max(1, snap(Math.round(sp.height)));
860-
// Pin the anchored edge: when dragging left/top, derive x/y from the
861-
// snapped size so the opposite edge doesn't walk by the snap delta.
862-
const edges = activeEdgesRef.current;
863-
const final = {
864-
x: Math.round(edges?.left ? cur.x + sp.width - width : cur.x),
865-
y: Math.round(edges?.top ? cur.y + sp.height - height : cur.y),
866-
props: { width, height },
867-
};
868-
updateObject(id, {
869-
x: sr.snapshot.x,
870-
y: sr.snapshot.y,
871-
props: { width: sr.snapshot.width, height: sr.snapshot.height },
872-
});
873-
temporal.resume();
874-
updateObject(id, final);
875-
} else {
857+
// try/finally so a throw can't leave undo paused (resume is idempotent).
858+
try {
859+
if (sr.changed && id && cur && !isGroup(cur)) {
860+
const sp = cur.props as { width: number; height: number };
861+
// Same snap/round/clamp contract as commitWidthHeightTransform, applied
862+
// to the already-baked float dims.
863+
const width = Math.max(1, snap(Math.round(sp.width)));
864+
const height = Math.max(1, snap(Math.round(sp.height)));
865+
// Pin the anchored edge: when dragging left/top, derive x/y from the
866+
// snapped size so the opposite edge doesn't walk by the snap delta.
867+
const edges = activeEdgesRef.current;
868+
const final = {
869+
x: Math.round(edges?.left ? cur.x + sp.width - width : cur.x),
870+
y: Math.round(edges?.top ? cur.y + sp.height - height : cur.y),
871+
props: { width, height },
872+
};
873+
updateObject(id, {
874+
x: sr.snapshot.x,
875+
y: sr.snapshot.y,
876+
props: { width: sr.snapshot.width, height: sr.snapshot.height },
877+
});
878+
temporal.resume();
879+
updateObject(id, final);
880+
}
881+
} finally {
876882
temporal.resume();
883+
cleanupTransformState();
877884
}
878-
cleanupTransformState();
879885
return;
880886
}
881887
if (selectedIds.length !== 1 || !selectedIds[0] || !stageRef.current) {

0 commit comments

Comments
 (0)