Skip to content

Conversation

@nikhilb4a
Copy link
Contributor

@nikhilb4a nikhilb4a commented Jan 29, 2026

Overview

https://votingworks.slack.com/archives/C08AWNY93PB/p1769698111533159

We had a slack alert where an internal function of TipTap is crashing within pastedCells. Based on the error message indicating the function is finding an empty fragment when it didn't expect to, I think it has to do with the sanitize function returning an empty string. I haven't been able to reproduce as I've tried copying tables/cells which I had expected to trigger the issue.

That being said, I think there are two clear improvements to make to this function:

  1. We should not strip to an empty string, we should maintain at least on nbsp. This is the suspected key fix.
  2. The regex was capturing all trailing spaces, even without an nbsp. I had noted in the comment we should require an nbsp, but somewhere in my iterations I removed it from the logic, so we were stripping even plain whitespace without a nbsp.

Demo Video or Screenshot

Testing Plan

Tried manual testing by creating html files with table cells with nbsps, but I wasn't able to reproduce. I think it's probably worth just keeping an eye on it rather than continuing to try and repro, but open to suggestions for sure.

Checklist

  • I have prefixed my PR title with "VxDesign: ", "VxPollBook: ", or "HWTA: " if my change is specific to one of those products.
  • I have added logging where appropriate for any new user actions.
  • I have added the "user_facing_change" label to this PR, if relevant, to automate an announcement in #machine-product-updates.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates rich text paste sanitization to prevent ProseMirror/TipTap table paste crashes by ensuring trailing NBSP cleanup never produces an empty fragment, and by only trimming whitespace when an NBSP is actually present.

Changes:

  • Update trailing-NBSP stripping logic to require an NBSP in the trimmed suffix and to fall back to a single NBSP instead of an empty string.
  • Refresh and expand tests to cover the new “don’t strip plain trailing spaces” behavior and the NBSP-only fallback case.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
apps/design/frontend/src/rich_text_editor.tsx Adjusts trailing NBSP stripping regex and adds a non-empty fallback to avoid empty fragments during paste.
apps/design/frontend/src/rich_text_editor.test.tsx Updates/extends unit tests to match the new trimming rules and NBSP-only fallback output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 336 to 338
return text.replace(/[\u00A0\s]*\u00A0[\u00A0\s]*$/, '') || ' ';
}

Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback uses a literal NBSP character (' '), which is visually ambiguous and easy to accidentally modify during edits/reformatting. Consider using an explicit escape ('\u00A0') or a named constant (e.g., const NBSP = '\u00A0') for readability and to avoid hidden-character issues.

Suggested change
return text.replace(/[\u00A0\s]*\u00A0[\u00A0\s]*$/, '') || ' ';
}
return text.replace(/[\u00A0\s]*\u00A0[\u00A0\s]*$/, '') || NBSP;
}
const NBSP = '\u00A0';

Copilot uses AI. Check for mistakes.
Comment on lines +333 to +334
// one NBSP (unicode U+00A0). Falls back to a single NBSP rather than empty to
// avoid producing empty fragments that crash ProseMirror's pastedCells function.
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment references ProseMirror's pastedCells function, but the PR description mentions a TipTap/ProseMirror internal named pasteCells. To make this easier to search/verify later, please align the function name here to the actual upstream identifier (or link to the upstream source).

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed PR description pasteCells -> pastedCells

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant