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; }
@@ -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; }
@@ -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; }
@@ -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; }
@@ -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; }
@@ -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; }
@@ -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; }
+15 -5
View File
@@ -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; }
@@ -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; }
@@ -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; }
@@ -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; }
@@ -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; }
+6 -1
View File
@@ -318,12 +318,17 @@ export async function runAgentLoop({
log(`generate id=${event.id} action=${event.action} count=${event.count}`);
try {
// 1. Wrap the original element in the variant scaffold (deterministic CLI)
// wrapTarget can be a static {classes, tag, elementId} (test fixtures
// know what they pick) or a function (event) => target (real-use
// sessions: the agent must derive the selector from the picked
// element on the fly).
const target = typeof wrapTarget === 'function' ? wrapTarget(event) : wrapTarget;
const wrapInfo = await runWrap({
tmp,
scriptsDir,
id: event.id,
count: event.count,
...wrapTarget,
...target,
});
log(`wrapped: ${wrapInfo.file} insertLine=${wrapInfo.insertLine}`);
+85
View File
@@ -0,0 +1,85 @@
/**
* Attach an LLM-backed agent loop to a live-server that is already running
* in the current working directory. Reads port + token from the PID file
* (.impeccable-live.json) that live-server.mjs --background writes on boot.
*
* Usage (from the project root):
* ANTHROPIC_API_KEY=... node tools/live-loop.mjs
*
* Optional:
* IMPECCABLE_E2E_LLM_MODEL=claude-sonnet-4-6 (default: claude-haiku-4-5)
*
* Stop with Ctrl-C; the live-server keeps running until you call
* `node source/skills/impeccable/scripts/live-server.mjs stop`.
*/
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { runAgentLoop } from '../tests/live-e2e/agent.mjs';
import { createLlmAgent } from '../tests/live-e2e/agents/llm-agent.mjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = path.join(__dirname, '..');
const SCRIPTS_DIR = path.join(REPO_ROOT, 'source', 'skills', 'impeccable', 'scripts');
const PID_FILE = path.join(REPO_ROOT, '.impeccable-live.json');
if (!fs.existsSync(PID_FILE)) {
console.error(
`No live-server PID file at ${PID_FILE}. Start one first:\n` +
` node source/skills/impeccable/scripts/live-server.mjs --background\n` +
` node source/skills/impeccable/scripts/live-inject.mjs --port <PORT>`,
);
process.exit(1);
}
const { port, token } = JSON.parse(fs.readFileSync(PID_FILE, 'utf-8'));
console.log(`Attaching agent loop to live-server on :${port}`);
const agent = await createLlmAgent({
model: process.env.IMPECCABLE_E2E_LLM_MODEL,
log: (m) => console.log('[llm] ' + m),
});
if (!agent) {
console.error('ANTHROPIC_API_KEY is not set. Set it and re-run.');
process.exit(1);
}
const controller = new AbortController();
process.on('SIGINT', () => {
console.log('\nStopping agent loop (live-server stays running).');
controller.abort();
setTimeout(() => process.exit(0), 200);
});
process.on('SIGTERM', () => controller.abort());
console.log(
`Agent ready (model=${process.env.IMPECCABLE_E2E_LLM_MODEL || 'claude-haiku-4-5'}).\n` +
`Pick an element in the browser and hit Go. Ctrl-C to stop.`,
);
await runAgentLoop({
tmp: REPO_ROOT,
scriptsDir: SCRIPTS_DIR,
port,
token,
agent,
signal: controller.signal,
log: (m) => console.log('[agent] ' + m),
// Per-event wrap target derived from the picked element's payload.
// Preference: id > first class > tag-only.
wrapTarget: (event) => {
const el = event.element || {};
const out = {};
if (el.id) out.elementId = el.id;
else if (Array.isArray(el.classes) && el.classes.length > 0) {
// live-wrap matches when ALL listed classes are present on the source
// node, so include only the first to maximize match likelihood.
out.classes = el.classes[0];
}
if (el.tagName) out.tag = el.tagName.toLowerCase();
return out;
},
});