perf(text): improve rich text performance#5658
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
1 Skipped Deployment
|
| export function isEmptyRichText(richText: TLRichText) { | ||
| if (richText.content.length === 1) { | ||
| if (!(richText.content[0] as any).content) return true | ||
| } | ||
| return false | ||
| } | ||
|
|
||
| export function isSingleCharacterRichText(richText: TLRichText) { | ||
| if (richText.content.length === 1) { | ||
| if ((richText.content[0] as any)?.content?.length === 1) return true | ||
| } | ||
| return false | ||
| } |
There was a problem hiding this comment.
these are fine for us, but we should double-check they work with non-texty extensions people might add like images, mentions, or horizontal rules
| const editor = useEditor() | ||
| const rInput = useRef<HTMLDivElement>(null) | ||
| const isEmpty = richText ? renderPlaintextFromRichText(editor, richText).length === 0 : true | ||
| const isEmpty = richText && !isEmptyRichText(richText) |
There was a problem hiding this comment.
patching this up, this was inverted
| if (prevPlaintext && !nextPlaintext) { | ||
| const wasEmpty = isEmptyRichText(prev.props.richText) | ||
| const isEmpty = isEmptyRichText(next.props.richText) | ||
| if (wasEmpty && !isEmpty) { |
There was a problem hiding this comment.
going to fix this inversion too :)
|
|
||
| export function isSingleCharacterRichText(richText: TLRichText) { | ||
| if (richText.content.length === 1) { | ||
| if ((richText.content[0] as any)?.content?.length === 1) return true |
There was a problem hiding this comment.
this should be checking the text
| const isEmptyTextShape = | ||
| this.editor.isShapeOfType<TLTextShape>(editingShape, 'text') && | ||
| renderPlaintextFromRichText(this.editor, editingShape.props.richText).trim() === '' | ||
| isEmptyRichText(editingShape.props.richText) |
There was a problem hiding this comment.
this isn't quite the same - we want the shapes to be removed if they're just whitespace, going to revert this bit
There was a problem hiding this comment.
Good catch, I'd left the one in onShapeUpdate or EditEnd or whatever that deletes the shape, though with a note that we should see if we can do a version of tiptap/pm's getText that bails as soon as it finds any text, so that we don't block after editing large pieces of text.
| return false | ||
| } | ||
|
|
||
| export function isSingleCharacterRichText(richText: TLRichText) { |
There was a problem hiding this comment.
actually, i'm gonna nix this — it's too surgical and we call this rarely.
|
Looks good, landing! Thanks for the help @mime |
This PR improves the performance of shapes with rich text content. ## The bug Due to reasons I cannot understand, we extract the fonts from rich text on every frame while translating shapes. Maybe we shouldn't, I don't know. Anyway, this is expensive when many shapes are moving around. Actually, not even that many. Try it yourself! ## The fix The fix here is the same as #5658, which is to skip the work if the text is empt; or use a weak cache to re-use the result from last time. ### Change type - [ ] `bugfix` - [x] `improvement` - [ ] `feature` - [ ] `api` - [ ] `other` ### Test plan 1. Create lots of geo shapes. 2. Drag all the geo shapes around. ### Release notes - Improved performance while editing many geo shapes or text shapes. --------- Co-authored-by: Mime Čuvalo <[email protected]>
|
Hey! 👋 This PR contains changes to Here's an example: Once you add this section, the check will pass automatically! |
This PR improves the performance of the app with regard to text.
Kapture.2025-03-15.at.14.22.33.mp4
The
renderPlaintextFromRichTextmethod is slow. It depends on TipTap'sgetTextmethod. This parses the rich text and flattens it to plain text. As usual, we can improve performance by:renderPlaintextFromRichTextfasterrenderPlaintextFromRichTextMaking it faster
We can make it faster by using a weak cache so that we don't need to re-flatten text if we've flattened it before. We can also make it faster by skipping the flatten if the text content is empty. This can be done very quickly without using
getText.Avoiding it entirely
It turns out that most of the places where we call
renderPlaintextFromRichTextare checks against empty text, meaning we don't need to callrenderPlaintextFromRichTextat all. In these cases we just check whether the text is empty and continue based on that result.Change type
improvementTest plan
Release notes