Skip to content

fix(canvas): decode unicode escapes in freeform sandbox JSX text#3214

Merged
k11kirky merged 2 commits into
mainfrom
posthog-code/fix-canvas-unicode-escapes
Jul 7, 2026
Merged

fix(canvas): decode unicode escapes in freeform sandbox JSX text#3214
k11kirky merged 2 commits into
mainfrom
posthog-code/fix-canvas-unicode-escapes

Conversation

@k11kirky

@k11kirky k11kirky commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Problem

Freeform canvases render literal \uXXXX escape sequences in visible text (e.g. "Survey started Jun 20, 2026 · live") instead of the decoded characters. Generated canvas source sometimes contains these escapes in JSX text, and JSX doesn't process them — so they show up verbatim, including in canvases that were already saved with escapes baked in.

Changes

Added a small Babel pre-pass to the sandbox bootstrap in sandboxRuntime.ts that decodes \uXXXX / \u{...} escapes in JSX text nodes and JSX attribute string values before the JSX transform runs. Escapes inside real JS string/template literals are untouched (Babel already decodes those). Because it runs at transpile time in the iframe, it fixes both newly generated and previously saved canvases — no migration needed.

How did you test this?

  • Ran the plugin through @babel/standalone@7.26.4 (the pinned sandbox version) against a repro including the reported string — \u00b7 decodes to ·, attribute values decode, and \u{...} code points / surrogate pairs work; escapes in JS string literals and invalid sequences are left intact.
  • pnpm --filter @posthog/ui typecheck, biome lint, and all 1232 @posthog/ui unit tests pass.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog Code

Generated canvases sometimes contain \uXXXX escapes in JSX text and
attribute strings, which JSX renders verbatim. Decode them in a Babel
pre-pass inside the sandbox so both new and already-saved canvases
render the intended glyphs. Escapes in real JS string literals are
left untouched.

Generated-By: PostHog Code
Task-Id: 20fa55b3-faa2-41b2-91a4-a6bab80ec174
@trunk-io

trunk-io Bot commented Jul 7, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 68f692b.

@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "fix(canvas): decode unicode escapes in f..." | Re-trigger Greptile

Comment on lines +277 to +304
const decodeUnicodeEscapes = (value) =>
value.replace(
/\\\\u\\{([0-9a-fA-F]{1,6})\\}|\\\\u([0-9a-fA-F]{4})/g,
(match, braced, plain) => {
try {
return String.fromCodePoint(parseInt(braced || plain, 16));
} catch {
return match;
}
},
);
const jsxUnicodeEscapesPlugin = () => ({
visitor: {
JSXText(path) {
path.node.value = decodeUnicodeEscapes(path.node.value);
},
JSXAttribute(path) {
const v = path.node.value;
if (v && v.type === "StringLiteral") {
const decoded = decodeUnicodeEscapes(v.value);
if (decoded !== v.value) {
v.value = decoded;
v.extra = undefined; // drop stale raw so the decoded value is emitted
}
}
},
},
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Missing tests for the new decode logic

The decodeUnicodeEscapes function and jsxUnicodeEscapesPlugin contain non-trivial regex logic with several distinct cases (4-hex \uXXXX, braced \u{...}, surrogate pairs, invalid code points that should pass through unchanged, escapes already processed by Babel that should be left alone). There are no new automated tests covering any of these paths. The custom instruction says parameterised tests are always preferred — a table-driven test over representative inputs would guard against regressions here.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread packages/ui/src/features/canvas/freeform/sandboxRuntime.ts Outdated
Lift the decoder out of the bootstrap string as an exported function
(interpolated back in via toString) so it can be unit-tested, add
parameterised tests over the decode cases, and only write JSXText
values back when they actually changed. Addresses Greptile review.

Generated-By: PostHog Code
Task-Id: 20fa55b3-faa2-41b2-91a4-a6bab80ec174
@k11kirky k11kirky marked this pull request as ready for review July 7, 2026 09:41
@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Reviews (2): Last reviewed commit: "test(canvas): cover unicode-escape decod..." | Re-trigger Greptile

@k11kirky k11kirky merged commit af58a5b into main Jul 7, 2026
28 checks passed
@k11kirky k11kirky deleted the posthog-code/fix-canvas-unicode-escapes branch July 7, 2026 10:29
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.

2 participants