Skip to content

fix(live): scan JSX comment variant markers; never whole-file-inject raw JSX (#454) - #455

Draft
kticoder wants to merge 2 commits into
pbakaus:mainfrom
kticoder:fix/jsx-variant-markers-454
Draft

fix(live): scan JSX comment variant markers; never whole-file-inject raw JSX (#454)#455
kticoder wants to merge 2 commits into
pbakaus:mainfrom
kticoder:fix/jsx-variant-markers-454

Conversation

@kticoder

@kticoder kticoder commented Jul 30, 2026

Copy link
Copy Markdown

Fixes #454.

Problem

injectVariantsFromSource() scans only for HTML comment markers, but live-wrap.mjs necessarily emits JSX comment markers ({/* impeccable-variants-start ID */}) inside JSX/TSX wrappers (line ~322; an HTML comment is invalid in a JSX element tree). On React targets the scan always misses, so the no-HMR fallback injects the entire raw source file, rendering {expressions} and marker text as literal page content.

Fix (kept minimal)

  1. Scan both marker syntaxes (<!-- --> and {/* */}) before falling back.
  2. For .jsx/.tsx sources with no markers, treat it like a missing wrapper (existing recovery/orphan path) instead of whole-file injection — raw JSX can never render correctly, so recovery is strictly better than showing source text. HTML behavior is unchanged.
  3. normalizeSourceFallbackBlock: strip JSX comments so {/* Original */} etc. don't render as text in the DOMParser preview.

Testing

Not run: bun run build / live-e2e (no bun locally) — happy to iterate if CI flags the generated outputs.

🤖 Generated with Claude Code


Note

Medium Risk
Changes the live cycling injection path for JSX/React, but behavior is narrowly scoped to marker parsing and adoption with existing recovery retries preserved.

Overview
Fixes React/JSX live variant injection when the no-HMR source fallback runs: the browser no longer treats raw .jsx/.tsx as injectable HTML or misses JSX-only variant markers.

For JSX/TSX, injectVariantsFromSource now adopts an existing live DOM wrapper (after Vite reload / Fast Refresh) instead of cloning a DOMParser preview, which previously showed {expressions} as literal text and broke React’s tree (#454).

When fetching source, variant blocks are found via both <!-- ... --> and {/* ... */} markers; JSX blocks get a synthesized wrapper when markers sit inside the element tree. Unmarked JSX skips whole-file injection and uses the existing empty/orphan recovery path; HTML unmarked behavior is unchanged. normalizeSourceFallbackBlock also strips JSX comments from DOMParser previews so comment markers do not render as visible text.

Reviewed by Cursor Bugbot for commit 88d501c. Bugbot is set up for automated code reviews on this repo. Configure here.

…SX source

injectVariantsFromSource only scanned for HTML comment markers
(<!-- impeccable-variants-start -->), but JSX/TSX wrappers necessarily
carry JSX comment markers ({/* ... */}) since HTML comments are invalid
inside a JSX element tree. The scan therefore always missed on React
targets and fell through to whole-file injection, rendering raw JSX
({expressions}, marker text) as literal page content.

- Scan both marker syntaxes before giving up.
- For .jsx/.tsx sources with no markers, treat as missing wrapper
  (recovery/orphan path) instead of injecting the entire raw file.
- Strip JSX comments in normalizeSourceFallbackBlock so they do not
  render as text in the DOMParser preview.

Fixes pbakaus#454
@kticoder
kticoder requested a review from pbakaus as a code owner July 30, 2026 11:48
Comment thread skill/scripts/live-browser.js
Comment on lines +6218 to +6221
const markerPairs = [
{ open: '<!--', close: '-->' },
{ open: '{/*', close: '*/}' },
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Source-fallback branches lack regression coverage

The new HTML/JSX marker extraction, markerless JSX/TSX skip, markerless HTML fallback, and JSX-comment normalization paths have no behavioral regression coverage. The focused suite remains green after each behavior is removed, so a future change can restore raw JSX injection or corrupt fallback previews without detection. Add focused inputs for both marker syntaxes, markerless .jsx and .tsx files, markerless HTML, and JSX comments before parsing.

Context Used: AGENTS.md (source)

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!

Artifacts

Implementation diff without matching test changes

  • Compared the parent and PR revisions for the changed browser source and focused test file; it shows the new marker/fallback/comment-removal code and no test-file change, confirming the coverage gap.

Focused live-browser source suite passing on the PR revision

  • Ran `node --test tests/live-browser-source.test.mjs` in `/home/user/repo`; all 8 tests passed, confirming the existing narrow suite is green but does not establish coverage of the new branches.

Focused suite passes after each changed fallback behavior is removed

  • Created temporary copies outside the repository, removed JSX marker extraction, markerless JSX skip, markerless HTML fallback, and JSX comment stripping one at a time, and ran the focused suite; every mutation passed 8/8, proving these behaviors lack regression detection.

Parent revision leaves a JSX comment in normalized source

  • Executed the focused normalization harness against `832b3742^`; JSX comment text remained in the output, establishing the pre-change behavior.

PR revision removes a JSX comment while preserving HTML input

  • Executed the focused normalization harness against the PR revision; JSX comment text was removed and equivalent HTML was unchanged, demonstrating the changed behavior.

Search results for direct regression coverage of changed fallback paths

  • Searched all test files for the new helper, marker-pair parsing, markerless JSX warning, and JSX marker syntax; results contain only existing generic source-contract references, not representative behavioral cases.

View artifacts

T-Rex Ran code and verified through T-Rex

Fix in Codex Fix in Claude Code

@greptile-apps

greptile-apps Bot commented Jul 30, 2026

Copy link
Copy Markdown

Greptile Summary

Improves JSX/TSX live-mode recovery by adopting framework-rendered variant wrappers after reloads, recognizing JSX comment markers, creating the missing wrapper around extracted JSX marker content, and preventing raw markerless JSX from being inserted into the page.

Runtime checks exercised all affected recovery paths: JSX marker blocks resumed cycling and advanced to the second variant without rendering marker text; markerless JSX did not create a wrapper or display raw JSX; and an existing live wrapper remained intact without a source request.

Confidence Score: 3/5

T-Rex T-Rex Logs

What T-Rex did

  • Executed the JSX source fallback runtime with PLAYWRIGHT_BROWSERS_PATH=/ms-playwright and the process exited successfully, validating the end-to-end flow for the pr455-jsx-source-fallback-runtime test.
  • Verified the output behavior across the run: JSX comment markers produce a synthesized wrapper and cycle to 2/2, markerless JSX yields no wrapper or visible content, and a live JSX wrapper retains data-live-identity="keep" with zero source fetches, as described in skill/scripts/live-browser.js:6206-6427.

T-Rex Ran code and verified through T-Rex

Reviews (3): Last reviewed commit: "fix(live): adopt live DOM wrapper for JS..." | Re-trigger Greptile

… around JSX marker block

The first pass fixed marker syntax but exposed two follow-ups on real
React targets:

1. JSX wrappers carry their markers INSIDE the wrapper div (live-wrap.mjs
   JSX branch), so the extracted block lacked the wrapper element and
   resume logged 'Variant wrapper not found in source file', stranding
   the bar. Synthesize the wrapper element around the extracted block.

2. On a Vite full reload React has already re-rendered the wrapper with
   evaluated expressions. Replacing it with a DOMParser clone of raw
   source would render {expressions} literally and detach React from its
   nodes. Adopt the live DOM wrapper instead: count variants, restore the
   visible index, and resume CYCLING without touching the tree.
if (arrivedVariants <= 0) {
if (state === 'GENERATING' && !opts.generationCompleted) return;
recoverEmptyCycling('jsx-dom-adopt-empty');
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Empty JSX adopt skips retries

High Severity

When a JSX live wrapper is present but still has zero variants after generationCompleted, the adopt path calls recoverEmptyCycling immediately. The source-fallback path below retries several times for the same stale-scaffold case, so a wrap that HMR’d before variants can tear the session down instead of waiting for Fast Refresh.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 88d501c. Configure here.

} else {
block = html;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fetch path still replaces React

High Severity

The new JSX adopt guard only runs synchronously before fetch. After the source download, an existingWrapper that appeared via HMR is still replaceChild’d with a DOMParser clone—the same React detach / literal {expression} failure #454 describes.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 88d501c. Configure here.

} else {
block = html;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

JSX no-marker skips recovery

Medium Severity

For .jsx/.tsx with no markers, block is set to '' so the wrapper lookup fails. That branch only discards when orphanDiscard is set; the generationCompleted done-fallback caller just returns, leaving the tab stuck in GENERATING instead of recovering.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 88d501c. Configure here.

Comment thread skill/scripts/live-browser.js

pbakaus commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Thanks for taking the time to contribute. This PR links issue #454, but I could not find explicit authorization from @pbakaus or @abdulwahabone for this implementation. This repository requires maintainer approval before implementation, so I’m closing the PR for now. This check was performed automatically by Codex; if this classification is mistaken, a maintainer can correct it.

@pbakaus pbakaus closed this Jul 30, 2026
@abdulwahabone

Copy link
Copy Markdown
Collaborator

Reopening after approval

@abdulwahabone abdulwahabone reopened this Aug 3, 2026
@abdulwahabone
abdulwahabone marked this pull request as draft August 3, 2026 07:19

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 88d501c. Configure here.

// JSX wrappers carry their markers INSIDE the wrapper div, so the
// extracted block lacks the wrapper element; synthesize it (#454).
block = '<div data-impeccable-variants="' + sessionId + '" style="display: contents">' + block + '</div>';
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Synthesized wrapper drops attributes

Medium Severity

When JSX markers match, the rebuilt wrapper only sets data-impeccable-variants and style. The real open tag outside those markers also carries data-impeccable-variant-count and, for insert sessions, data-impeccable-mode="insert", so source-fallback injection loses planned count and insert-mode behavior.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 88d501c. Configure here.

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the PR. Impeccable is moving quickly, and this PR is currently waiting on contributor action.

It has been waiting for contributor action for 7 days. Please address the outstanding review feedback, draft state, or explicit maintainer wait request. PRs that are still waiting on contributor action after 14 days are closed automatically.

If nothing changes, this PR may be closed on or after 2026-08-17. Happy to reopen when it is ready to continue.

@github-actions github-actions Bot added the stale Inactive PR that may be closed soon label Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked: review threads Unresolved review feedback or requested changes remain policy: approved policy: needs approval stale Inactive PR that may be closed soon waiting on contributor Waiting for the PR author to respond or make changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Live mode: injectVariantsFromSource renders raw JSX as text for React targets (JSX comment markers never match HTML comment scan)

4 participants