Closes#114.
Three orthogonal bugs that surfaced together when live mode picked an
element inside a Vite React/TSX component with sibling branches:
1. JSX wrapper insertion produced invalid TSX
- Replacing a single picked JSX child with [comment, <div>, comment]
yields three adjacent siblings, which oxc rejects with "Adjacent
JSX elements must be wrapped in an enclosing tag."
- A Fragment `<></>` solves the adjacency case but breaks
`cloneElement`-using parents (Radix `asChild`, Headless UI, etc.)
with "Invalid prop supplied to React.Fragment."
- Fix: keep the wrapper `<div data-impeccable-variants="ID">` as the
single JSX-slot child and tuck both marker comments INSIDE it.
accept/discard now expands its replacement range to include the
wrapper's `<div>` open/close lines via div-depth tracking.
2. carbonize produced nested template literals in TSX `<style>`
- extractCss captured `{` / `` `} `` lines from the agent's existing
`<style>{`…`}</style>` template, then handleAccept re-wrapped with
another pair, producing `<style>{`{`@scope…`}`}</style>` which oxc
rejects with "Expected `}` but found `@`".
- Fix: extractCss now strips a leading `{` and trailing `` `} ``
wherever they appear in the captured content (own line OR attached
to the first/last CSS line), so re-wrapping always yields exactly
one `{` ` … ` `}` pair.
3. Ambiguous source matching for repeated JSX branches
- `findElement` returned the first substring match. Multiple
`<aside className="card">` siblings all matched the same query, so
wrap silently landed on the first regardless of which one the user
picked.
- Fix: live-wrap accepts `--text TEXT` (the picked element's
textContent), collects ALL candidates via `findAllElements`, and
narrows by a tag-stripped, JSX-expression-stripped substring match.
Returns `element_ambiguous + candidates[]` when multiple branches
match equally; falls back to first-match when source uses dynamic
content (`<h1>{title}</h1>`) so existing flows aren't broken.
- The fake e2e agent now forwards `event.element.textContent` to
wrap, and live.md tells the agent to do the same.
Test coverage:
- New `vite8-react-tsx-repeated-aside` e2e fixture: three identical
`<aside>` branches, picks the second card's <h1>, runs the full
wrap → Go → cycle → accept → carbonize cycle on a real Vite + TSX
dev server, asserts that Hero One and Hero Three survive untouched
(proving wrap landed on the correct branch).
- Six new unit tests across live-wrap.test.mjs and live-accept.test.mjs
covering the Fragment-replacement design, both leading/trailing
template-literal placements, --text disambiguation, the dynamic-
content fallback, and the element_ambiguous error shape.
- New `runtime.assertSourceContains` fixture hook so other regression
fixtures can assert sibling-branch survivability cheaply.
All 186 unit + static-fixture tests pass; all 21 live e2e fixtures
(20 prior + new TSX) pass with no console errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The configure row's text input filled its background with translucent
magenta (BP.accentSoft) on focus. Composited against the dark bar surface
this produced a murky purple where the browser's default placeholder
gray washed out — flagged in a real session as "godawful styling, gray
text on dark magenta really hurts my eyes". Fix: focus state shows an
accent-colored border only, no fill; placeholder color is set explicitly
to BP.textDim via a one-shot stylesheet so it reads in both themes.
tests/live-e2e/agent.mjs: runAgentLoop's wrapTarget now accepts either a
static {classes,tag,elementId} (test fixture mode) OR a function that
derives the target from each generate event (real-use mode where the
picked element is unknown ahead of time).
tools/live-loop.mjs: standalone runner that attaches the LLM agent to a
running live-server. Used as a test-harness shortcut for validating live
mode out of band; in production the user's coding agent (Claude Code,
Cursor, etc.) plays this role directly via the live skill spec.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
19 fixtures (11 styling/build variants + 4 conditional-render scenarios + 4
meta-frameworks) drive the entire user flow end-to-end: handshake, pick,
configure, Go, cycle, accept, carbonize cleanup. Each fixture installs real
deps, boots the framework dev server, and runs Playwright Chromium against a
deterministic fake agent that produces realistic variants (colocated style
with @scope rules, full data-impeccable-params manifests covering range +
steps + toggle, JSX/HTML/Svelte syntax-aware rendering).
The agent is pluggable via a one-method interface — generateVariants(event) —
so a future LLM-backed agent slots in by implementing the same shape. The
orchestrator handles wrap, file write, accept, and carbonize cleanup
deterministically regardless of which agent is plugged in.
Schema extensions (tests/framework-fixtures/README.md): runtime block adds
preActions / reloadProbe / pickSelector / scheme / ignoreHTTPSErrors so
fixtures can drive conditional UI (modal, tab, route) before pick and verify
the carbonized variant survives a reload.
Static fixture suite filtered to skip dirs without fixture.json so empty
scaffold dirs no longer break discovery. Total: 178 static checks, 19 E2E
full cycles, ~107s wall clock for the E2E suite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>