diff --git a/.agents/skills/impeccable/reference/live.md b/.agents/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.agents/skills/impeccable/reference/live.md +++ b/.agents/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.agents/skills/impeccable/scripts/live-browser.js b/.agents/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.agents/skills/impeccable/scripts/live-browser.js +++ b/.agents/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.claude/skills/impeccable/reference/live.md b/.claude/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.claude/skills/impeccable/reference/live.md +++ b/.claude/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.claude/skills/impeccable/scripts/live-browser.js b/.claude/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.claude/skills/impeccable/scripts/live-browser.js +++ b/.claude/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.codex/skills/impeccable/reference/live.md b/.codex/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.codex/skills/impeccable/reference/live.md +++ b/.codex/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.codex/skills/impeccable/scripts/live-browser.js b/.codex/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.codex/skills/impeccable/scripts/live-browser.js +++ b/.codex/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.cursor/skills/impeccable/reference/live.md b/.cursor/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.cursor/skills/impeccable/reference/live.md +++ b/.cursor/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.cursor/skills/impeccable/scripts/live-browser.js b/.cursor/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.cursor/skills/impeccable/scripts/live-browser.js +++ b/.cursor/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.gemini/skills/impeccable/reference/live.md b/.gemini/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.gemini/skills/impeccable/reference/live.md +++ b/.gemini/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.gemini/skills/impeccable/scripts/live-browser.js b/.gemini/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.gemini/skills/impeccable/scripts/live-browser.js +++ b/.gemini/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.kiro/skills/impeccable/reference/live.md b/.kiro/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.kiro/skills/impeccable/reference/live.md +++ b/.kiro/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.kiro/skills/impeccable/scripts/live-browser.js b/.kiro/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.kiro/skills/impeccable/scripts/live-browser.js +++ b/.kiro/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.opencode/skills/impeccable/reference/live.md b/.opencode/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.opencode/skills/impeccable/reference/live.md +++ b/.opencode/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.opencode/skills/impeccable/scripts/live-browser.js b/.opencode/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.opencode/skills/impeccable/scripts/live-browser.js +++ b/.opencode/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.pi/skills/impeccable/reference/live.md b/.pi/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.pi/skills/impeccable/reference/live.md +++ b/.pi/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.pi/skills/impeccable/scripts/live-browser.js b/.pi/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.pi/skills/impeccable/scripts/live-browser.js +++ b/.pi/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.rovodev/skills/impeccable/reference/live.md b/.rovodev/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.rovodev/skills/impeccable/reference/live.md +++ b/.rovodev/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.rovodev/skills/impeccable/scripts/live-browser.js b/.rovodev/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.rovodev/skills/impeccable/scripts/live-browser.js +++ b/.rovodev/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.trae-cn/skills/impeccable/reference/live.md b/.trae-cn/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.trae-cn/skills/impeccable/reference/live.md +++ b/.trae-cn/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.trae-cn/skills/impeccable/scripts/live-browser.js b/.trae-cn/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.trae-cn/skills/impeccable/scripts/live-browser.js +++ b/.trae-cn/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/.trae/skills/impeccable/reference/live.md b/.trae/skills/impeccable/reference/live.md index ba0e9b0c5..34227e9fa 100644 --- a/.trae/skills/impeccable/reference/live.md +++ b/.trae/skills/impeccable/reference/live.md @@ -3,7 +3,6 @@ Launch interactive live variant mode: select elements in the browser, pick a des ## Prerequisites - A running development server with hot module replacement (Vite, Next.js, Bun, etc.), OR a static HTML file open in the browser -- The impeccable CLI installed (`npm i -g impeccable`) ## Start the Server @@ -49,12 +48,12 @@ If browser automation tools are available, also navigate to the page so the user ## Enter the Poll Loop -Run a blocking poll loop. On each iteration, wait for a browser event and respond: +Run the poll as a **background task** if your harness supports it (Claude Code does). This keeps the main conversation free for other work while waiting for browser events. Do NOT set a timeout: the poll should wait indefinitely until the user acts. ``` LOOP: - Run: node {{scripts_path}}/live-poll.mjs - Read the JSON output. Dispatch based on the "type" field: + Run (background, no timeout): node {{scripts_path}}/live-poll.mjs + When the task completes, read the JSON output. Dispatch based on the "type" field: TYPE "generate": → See "Handle Generate" below @@ -170,6 +169,17 @@ The event contains: `{id}`. node {{scripts_path}}/live-poll.mjs --reply SESSION_ID done ``` +## Stopping Live Mode + +The user can stop live mode in several ways: +- Saying "stop live mode" or "exit live" in the conversation +- Closing the browser tab (the SSE connection drops, poll returns `exit` after 8s) +- The browser's exit button (when the global bar is implemented) + +When the user asks to stop, or the poll returns `exit`, proceed to Cleanup below. + +If the poll is still running as a background task, kill it and proceed directly to cleanup. + ## Cleanup (on exit) When the loop ends: diff --git a/.trae/skills/impeccable/scripts/live-browser.js b/.trae/skills/impeccable/scripts/live-browser.js index 72534661b..c03dbdc48 100644 --- a/.trae/skills/impeccable/scripts/live-browser.js +++ b/.trae/skills/impeccable/scripts/live-browser.js @@ -1076,9 +1076,8 @@ if (!currentSessionId) return; sendEvent({ type: 'discard', id: currentSessionId }); markSessionHandled(); - state = 'SAVING'; - updateBarContent('saving'); - // Wait for "done" WS message to show confirmation and dismiss + // Discard dismisses immediately (no "Applying" state, the agent just cleans up) + cleanup(); } // --------------------------------------------------------------------------- @@ -1135,6 +1134,26 @@ } function cleanup() { + // Remove any leftover variant wrapper from the live DOM. + // After discard, the agent cleans the source, but on dev servers without + // HMR the DOM still has the old wrapper, which confuses the picker. + if (currentSessionId) { + const wrapper = document.querySelector('[data-impeccable-variants="' + currentSessionId + '"]'); + if (wrapper) { + // Restore the original element into the DOM + const orig = wrapper.querySelector('[data-impeccable-variant="original"]'); + if (orig) { + const content = orig.firstElementChild; + if (content) { + wrapper.parentElement.replaceChild(content, wrapper); + } else { + wrapper.remove(); + } + } else { + wrapper.remove(); + } + } + } hideBar(); hideHighlight(); stopScrollTracking(); @@ -1199,14 +1218,21 @@ const variants = wrapper.querySelectorAll('[data-impeccable-variant]:not([data-impeccable-variant="original"])'); arrivedVariants = variants.length; - // Restore visible variant from localStorage if available, else default to 1 + // Restore state from localStorage if available const saved = loadSession(); - visibleVariant = (saved && saved.id === sessionId && saved.visible > 0 && saved.visible <= arrivedVariants) - ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved && saved.id === sessionId) { + visibleVariant = (saved.visible > 0 && saved.visible <= arrivedVariants) ? saved.visible : (arrivedVariants > 0 ? 1 : 0); + if (saved.action) selectedAction = saved.action; + if (saved.count) selectedCount = saved.count; + } else { + visibleVariant = arrivedVariants > 0 ? 1 : 0; + } - // Find the visible variant's content element for highlight positioning + // Find the visible variant's content element for highlight positioning. + // Try the visible variant first, fall back to the original's content. const visEl = wrapper.querySelector('[data-impeccable-variant="' + visibleVariant + '"] > :first-child'); - selectedElement = visEl || wrapper.parentElement; + const origEl = wrapper.querySelector('[data-impeccable-variant="original"] > :first-child'); + selectedElement = visEl || origEl || wrapper.parentElement; // Set display state BEFORE starting observer (avoid triggering it) if (visibleVariant > 0) showVariantInDOM(currentSessionId, visibleVariant); diff --git a/public/index.html b/public/index.html index 82c3f8385..9d47a9845 100644 --- a/public/index.html +++ b/public/index.html @@ -85,40 +85,40 @@
Design fluency for AI harnesses
+Design fluency for AI harnesses
-Great design prompts require design vocabulary. Most people don't have it. Impeccable teaches your AI deep design knowledge and gives you 22 commands to steer the result.
-Impeccable teaches your AI real design and gives you 22 commands to steer the result.
+Great design prompts require design vocabulary. Most people don't have it. Impeccable teaches your AI deep design knowledge and gives you 22 commands to steer the result.
+Impeccable teaches your AI real design and gives you 22 commands to steer the result.
-