Working on a new Undo / Redo manager that runs on mobx and JSON patches. Here's how it works! 🧵
First off, here's a CodeSandbox. Looks pretty simple but there's a lot going on. I've included my tests! codesandbox.io/s/mobx-undo-re…
In our system, we're tracking changes to a "document". Each time the document changes, we generate a "snapshot" and compare it with our previous snapshot in order to create a "patch" that describes how to get from the current snapshot back to the previous.
These patches are "JSON patches" (jsonpatch.com). Each patch is made up of a series of operations that will turn one object into another. In our case, our patches describe all of the operations needed to get from our current snapshot back to our previous snapshot.
We store these patches in a "stack". This is a list that has a "pointer" that refers to the current patch. We also keep around a snapshot from our last change (or the initial document, if we haven't changed anything yet).
1️⃣ Let's say we make a change to our document.
When our document changes, we first take a snapshot of the current document...
...and we compare it against our previous snapshot in order to create a new patch. Again, this patch describes all the steps needed to "undo" the new change.
Next, we push the new patch onto the stack and move the pointer up so that it points to our new patch.
Finally, we save the snapshot we made of our current document as our new previous snapshot. (We can discard the old previous snapshot.)
Now we're ready for the next change!
2️⃣ To undo a change, we apply the current patch (e.g. the one that the pointer is pointing to) to the current document. Applying a patch performs the patch's operations and will bring the state back to where it was before the most recent change.
Next, we take a snapshot of the new document...
And compare it with the previous snapshot in order to produce a new patch. This patch is our "redo", it lists the operations needed to go from our current document (after undoing) back to what it was before we pressed "undo".
We *replace* the current patch—the one we just applied—with this new "redo" patch, and move the pointer back to the previous patch.
And finally we set the current snapshot as our previous snapshot.
And again, we're ready for the next change!
3️⃣ If we pressed redo, then we would start by moving out pointer up.
We would then apply the current patch to the current document...
And then create a snapshot of the current document, compare it with the previous document to create a patch...
And then *replace* the current patch with the new one.
Finally, we'd set the current snapshot as the previous snapshot...
And job done!
But we're not done. The story continues! (in the next thread)
• • •
Missing some Tweet in this thread? You can try to
force a refresh
Build popular open source library, train
own model on docs + examples (some private?), guarantee that model is updated with every release, sell as integration with user IDEs
Let’s say @threejs went this route. The core product is free (wedge) but AI assisted coding environments sometimes trip over out of date versions or make poor choices based on bad examples in their training data.
The threejs team announced ThreeAssist, an “expert” model fine-tuned on each minor release, fresh docs, etc. Outscores commodity models, produces better results, guaranteed to be true to the given version, etc.
I see a lot of AI uses for the @tldraw SDK. I’d say about 25% of our customers are full on AI apps and another 30% are looking to integrate AI tools into their canvas in the future
No surprise, the most shippable / effective use cases are currently where generated artifacts can augment existing use-cases
Figma has a “wireframe view” that might help here as a fallback, if it means keeping images etc out of memory, though it would be up to the app to switch into that. (And actually I’m not even sure if that would work)
For tldraw we have a limit of shapes per page and pages per project but it’s still theoretically possible to crash it out via memory depending on your browser.
Here's the full interaction, complete with hover indicators.
|
Note that you can interact with text directly either: a) when editing text or b) when the text tool is selected. This will mean you can't create text on top of other text, but I'm guessing this is okay.
In @figma, holding shift while drawing a selection box over items will: 1. select deselected items 2. deselect selected items
Is this the right behavior? Have you ever accidentally selected / deselected items while shift-selecting?
@figma I remember working out some more complicated logic here with a rule like "if any new items are being added to the selection, don't deselect any other items"
Remind me next time to migrate the database before shipping runtime validation 💀
In tldraw’s beta db, there were lots of different versions of our data scattered around, including some from the wild times before we wrote client-side migrations, and some that just included broken data, x = NaN etc.
We’d written validation in order to catch this type of bad data when it came into the app. We didn’t write any recovery from the bad data, the app would just throw as soon as it ran into it.