Fix live detector empty state (#190)

* Fix live detector empty state

* Fix live detector stale scan results
This commit is contained in:
Abdul Wahab
2026-06-01 14:44:45 -07:00
committed by GitHub
parent 3f8d002b76
commit 69b5f3af49
44 changed files with 853 additions and 224 deletions
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
+25 -2
View File
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
+15 -7
View File
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
+15 -7
View File
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -1437,13 +1437,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1497,7 +1504,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -1565,7 +1572,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -1585,6 +1592,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -1599,11 +1607,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -1618,10 +1626,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -4253,13 +4253,20 @@ if (IS_BROWSER) {
return true;
}
function postSerializedFindings(groupMap) {
function scanResultMeta(options = {}) {
const scanId = options.scanId;
if (typeof scanId !== 'string' && typeof scanId !== 'number') return {};
return { scanId: String(scanId) };
}
function postSerializedFindings(groupMap, options = {}) {
if (!EXTENSION_MODE) return;
const allFindings = browserFindingsFromMap(groupMap);
window.postMessage({
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4313,7 +4320,7 @@ if (IS_BROWSER) {
rememberVisualContrastAnalysis(result);
const added = addVisualContrastResult(groupMap, result, { decorate: true });
if (added) {
postSerializedFindings(groupMap);
postSerializedFindings(groupMap, options);
window.dispatchEvent(new CustomEvent('impeccable-visual-contrast-resolved', {
detail: {
selector: result.selector,
@@ -4381,7 +4388,7 @@ if (IS_BROWSER) {
overlayIndex = 0;
}
function renderBrowserFindings(collected) {
function renderBrowserFindings(collected, options = {}) {
const { allFindings, pageLevelFindings } = collected;
for (const { el, findings } of allFindings) {
@@ -4401,6 +4408,7 @@ if (IS_BROWSER) {
source: 'impeccable-results',
findings: serializeFindings(allFindings),
count: allFindings.length,
...scanResultMeta(options),
}, '*');
}
@@ -4415,11 +4423,11 @@ if (IS_BROWSER) {
clearOverlays();
const generation = scanGeneration;
const collected = collectBrowserFindings();
const allFindings = renderBrowserFindings(collected);
const allFindings = renderBrowserFindings(collected, options);
if (shouldRunVisualContrast(options)) {
addVisualContrastFindings(collected.groupMap, options, { decorate: true, generation })
.then(() => {
if (generation === scanGeneration) postSerializedFindings(collected.groupMap);
if (generation === scanGeneration) postSerializedFindings(collected.groupMap, options);
})
.catch(err => {
reportVisualContrastError(err);
@@ -4434,10 +4442,10 @@ if (IS_BROWSER) {
if (shouldRunVisualContrast(options)) {
const collected = await collectBrowserFindingsAsync(options, { generation, scheduleLazy: true });
if (generation !== scanGeneration) return [];
return renderBrowserFindings(collected);
return renderBrowserFindings(collected, options);
}
lastVisualContrastAnalyses = [];
return renderBrowserFindings(collectBrowserFindings());
return renderBrowserFindings(collectBrowserFindings(), options);
};
const detect = function(options = {}) {
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
+25 -2
View File
@@ -6088,6 +6088,10 @@ void main() {
let steerFocusRecoverTimer = null;
const STEER_PAGE_FOCUS_PAUSE_MS = 500;
let detectActive = false;
let detectScanSeq = 0;
let activeDetectScanId = null;
let pendingDetectScanId = null;
const DETECT_EMPTY_MESSAGE = 'No detector issues found.';
const PICK_PREFS_KEY = 'impeccable-live-pick';
const INTERACTION_PREFS_KEY = 'impeccable-live-interaction';
const PLACEHOLDER_DEFAULT_HEIGHT = 80;
@@ -7601,6 +7605,17 @@ void main() {
let detectReady = false; // true once detect script posts 'impeccable-ready'
let detectPendingScan = false; // scan requested before script was ready
function requestDetectScan() {
const scanId = String(++detectScanSeq);
activeDetectScanId = scanId;
pendingDetectScanId = scanId;
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId },
}, '*');
}
function toggleDetect() {
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
detectActive = !detectActive;
@@ -7611,12 +7626,14 @@ void main() {
detectPendingScan = true;
loadDetectScript();
} else if (detectReady) {
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
} else {
detectPendingScan = true;
}
} else {
window.postMessage({ source: 'impeccable-command', action: 'remove' }, '*');
activeDetectScanId = null;
pendingDetectScanId = null;
detectCount = 0;
updateGlobalBarState();
}
@@ -7686,12 +7703,18 @@ void main() {
detectReady = true;
if (detectPendingScan && detectActive) {
detectPendingScan = false;
window.postMessage({ source: 'impeccable-command', action: 'scan' }, '*');
requestDetectScan();
}
}
// Scan results arrived
if (e.data.source === 'impeccable-results') {
if (!detectActive) return;
if (activeDetectScanId && e.data.scanId !== activeDetectScanId) return;
detectCount = e.data.count || 0;
if (detectActive && pendingDetectScanId && detectCount === 0) {
showToast(DETECT_EMPTY_MESSAGE, 3200);
}
pendingDetectScanId = null;
updateGlobalBarState();
}
}
@@ -577,6 +577,61 @@ describe('detectUrl — browser-only fixtures', () => {
}
});
it('extension mode echoes scan ids on result messages', async () => {
const puppeteer = await import('puppeteer');
const browser = await puppeteer.default.launch({
headless: true,
args: process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox'] : [],
});
try {
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800 });
await page.goto(`${baseUrl}/fixtures/antipatterns/should-pass.html`, { waitUntil: 'load' });
const browserScript = fs.readFileSync(path.join(ROOT, 'cli/engine/detect-antipatterns-browser.js'), 'utf-8');
await page.evaluate(() => {
document.documentElement.dataset.impeccableExtension = 'true';
window.__impeccableMessages = [];
window.addEventListener('message', event => {
if (event.source !== window || !event.data?.source?.startsWith('impeccable-')) return;
window.__impeccableMessages.push(event.data);
});
});
await page.evaluate(browserScript);
const result = await page.evaluate(async () => {
window.postMessage({
source: 'impeccable-command',
action: 'scan',
config: { scanId: 'scan-2' },
}, '*');
const deadline = Date.now() + 1000;
while (
Date.now() < deadline &&
!window.__impeccableMessages.some(message =>
message.source === 'impeccable-results' &&
message.scanId === 'scan-2'
)
) {
await new Promise(resolve => setTimeout(resolve, 25));
}
const resultMessage = window.__impeccableMessages.find(message =>
message.source === 'impeccable-results' &&
message.scanId === 'scan-2'
);
return {
ready: window.__impeccableMessages.some(message => message.source === 'impeccable-ready'),
scanId: resultMessage?.scanId || null,
count: resultMessage?.count ?? null,
};
});
assert.equal(result.ready, true, `expected extension ready message, got: ${JSON.stringify(result)}`);
assert.equal(result.scanId, 'scan-2', `expected scan id echo in results, got: ${JSON.stringify(result)}`);
assert.equal(result.count, 0, `expected clean fixture to have no findings, got: ${JSON.stringify(result)}`);
await page.close();
} finally {
await browser.close().catch(() => {});
}
});
it('browser API: impeccableDetect is pure, impeccableScan decorates', async () => {
const puppeteer = await import('puppeteer');
const browser = await puppeteer.default.launch({
+28
View File
@@ -292,6 +292,34 @@ describe('live-browser.js regression guards', () => {
);
});
it('detect mode shows an empty result toast once per requested scan', () => {
assert.match(
SOURCE,
/const DETECT_EMPTY_MESSAGE = 'No detector issues found\.';/,
'live detector zero result copy should live in one named constant',
);
assert.match(
SOURCE,
/function requestDetectScan\(\)[\s\S]{0,240}?const scanId = String\(\+\+detectScanSeq\);[\s\S]{0,80}?activeDetectScanId = scanId;[\s\S]{0,160}?config: \{ scanId \}/,
'Detect scans must send a fresh scan id to the detector',
);
assert.match(
SOURCE,
/if \(!detectActive\) return;[\s\S]{0,80}?if \(activeDetectScanId && e\.data\.scanId !== activeDetectScanId\) return;/,
'live detector results must ignore inactive and stale scan ids',
);
assert.match(
SOURCE,
/if \(detectActive && pendingDetectScanId && detectCount === 0\) \{[\s\S]{0,80}?showToast\(DETECT_EMPTY_MESSAGE, 3200\);[\s\S]{0,120}?pendingDetectScanId = null;/,
'a matching zero result scan must use the existing toast UI and clear the pending scan id',
);
assert.match(
SOURCE,
/window\.postMessage\(\{ source: 'impeccable-command', action: 'remove' \}, '\*'\);[\s\S]{0,80}?activeDetectScanId = null;[\s\S]{0,80}?pendingDetectScanId = null;/,
'turning Detect off must clear scan ids',
);
});
it('insert mode UI and generate payload guards', () => {
assert.match(SOURCE, /function toggleInsert\(\)/, 'global bar must expose insert toggle');
assert.match(SOURCE, /PREFIX \+ '-insert-toggle'/, 'insert toggle needs stable id');