fix(live): readable freeform input on dark bar + tools/live-loop.mjs

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>
This commit is contained in:
Paul Bakaus
2026-04-25 01:35:32 -07:00
co-authored by Claude Opus 4.7
parent 74f16d6310
commit 6e96f62803
14 changed files with 271 additions and 61 deletions
@@ -905,7 +905,12 @@
pill.addEventListener('click', (e) => { e.stopPropagation(); toggleActionPicker(); });
row.appendChild(pill);
// Freeform input
// Freeform input. Focus state shows an accent-colored border only —
// an earlier version tinted the background with `BP.accentSoft`, which
// composited against the dark bar surface to a murky purple where the
// browser's default placeholder gray was unreadable. Placeholder color
// is set explicitly via a one-shot stylesheet keyed off this input's id
// so it picks up the bar's `textDim` token in both themes.
const input = document.createElement('input');
input.id = PREFIX + '-input';
input.type = 'text';
@@ -916,15 +921,20 @@
border: '1px solid transparent', background: 'transparent',
fontFamily: FONT, fontSize: '12px', color: BP.text,
outline: 'none',
transition: 'border-color 0.15s ease, background 0.15s ease',
transition: 'border-color 0.15s ease',
});
if (!document.getElementById(PREFIX + '-input-style')) {
const s = document.createElement('style');
s.id = PREFIX + '-input-style';
s.textContent =
'#' + PREFIX + '-input::placeholder { color: ' + BP.textDim + '; opacity: 1; }';
document.head.appendChild(s);
}
input.addEventListener('focus', () => {
input.style.borderColor = BP.hairline;
input.style.background = BP.accentSoft;
input.style.borderColor = BP.accent;
});
input.addEventListener('blur', () => {
input.style.borderColor = 'transparent';
input.style.background = 'transparent';
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') { e.stopPropagation(); e.preventDefault(); handleGo(); return; }