The solution
There are a couple of ways to handle this and your approach should change depending on how your program works.
Squash or discard
One way to handle this would be to squash all of the changes into one undo entry. This means that when the user selects undo once, tlDraw will undo the colour change, undo the label, undo the line, and undo the bubble drag as if they were all one action. This may be the option you want, but not necessarily in this case.
In our instance, we have likely programmed the app to programmatically reassess lines, labels, and colours whenever anything changes, so we don’t actually need to undo those. Instead, we undo the user’s bubble drag, and everything else will recalculate automatically – But bare in mind, your app needs to be built to work like this (ie. after every tldraw change, it recalculates and adds/removes what’s needed).
Essentially, in this example, we can tell tlDraw’s undo stack to ignore any of the changes we did programmatically. To do this, when we make the changes, we pass a flag to the method to indicate it is ephemeral.
Solution code
editor.updateShapes([
{
id: shapeId1,
type: 'bubble',
props: {
color: 'green'
}
},
{
id: shapeId2,
type: 'bubble',
props: {
color: 'green'
}
}
], {
ephemeral: true,
});