diff --git a/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.agents/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.agents/skills/impeccable/scripts/detector/shared/color.mjs b/.agents/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.agents/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.agents/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.claude/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.claude/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.claude/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.claude/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.claude/skills/impeccable/scripts/detector/shared/color.mjs b/.claude/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.claude/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.claude/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.cursor/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.cursor/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.cursor/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.cursor/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.cursor/skills/impeccable/scripts/detector/shared/color.mjs b/.cursor/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.cursor/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.cursor/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.gemini/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.gemini/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.gemini/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.gemini/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.gemini/skills/impeccable/scripts/detector/shared/color.mjs b/.gemini/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.gemini/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.gemini/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.github/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.github/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.github/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.github/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.github/skills/impeccable/scripts/detector/shared/color.mjs b/.github/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.github/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.github/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.grok/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.grok/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.grok/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.grok/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.grok/skills/impeccable/scripts/detector/shared/color.mjs b/.grok/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.grok/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.grok/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.hermes/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.hermes/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.hermes/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.hermes/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.hermes/skills/impeccable/scripts/detector/shared/color.mjs b/.hermes/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.hermes/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.hermes/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.kiro/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.kiro/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.kiro/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.kiro/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.kiro/skills/impeccable/scripts/detector/shared/color.mjs b/.kiro/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.kiro/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.kiro/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.opencode/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.opencode/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.opencode/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.opencode/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.opencode/skills/impeccable/scripts/detector/shared/color.mjs b/.opencode/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.opencode/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.opencode/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.pi/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.pi/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.pi/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.pi/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.pi/skills/impeccable/scripts/detector/shared/color.mjs b/.pi/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.pi/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.pi/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.qoder/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.qoder/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.qoder/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.qoder/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.qoder/skills/impeccable/scripts/detector/shared/color.mjs b/.qoder/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.qoder/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.qoder/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.rovodev/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.rovodev/skills/impeccable/scripts/detector/shared/color.mjs b/.rovodev/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.rovodev/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.rovodev/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.trae-cn/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.trae-cn/skills/impeccable/scripts/detector/shared/color.mjs b/.trae-cn/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.trae-cn/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.trae-cn/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.trae/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.trae/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.trae/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.trae/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.trae/skills/impeccable/scripts/detector/shared/color.mjs b/.trae/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.trae/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.trae/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.vibe/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/.vibe/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/.vibe/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/.vibe/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/.vibe/skills/impeccable/scripts/detector/shared/color.mjs b/.vibe/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/.vibe/skills/impeccable/scripts/detector/shared/color.mjs +++ b/.vibe/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/plugin/skills/impeccable/scripts/detector/detect-antipatterns-browser.js b/plugin/skills/impeccable/scripts/detector/detect-antipatterns-browser.js index 8d5bce3f6..e3e5dc21c 100644 --- a/plugin/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +++ b/plugin/skills/impeccable/scripts/detector/detect-antipatterns-browser.js @@ -773,14 +773,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 }); diff --git a/plugin/skills/impeccable/scripts/detector/shared/color.mjs b/plugin/skills/impeccable/scripts/detector/shared/color.mjs index d2524ce52..983e15330 100644 --- a/plugin/skills/impeccable/scripts/detector/shared/color.mjs +++ b/plugin/skills/impeccable/scripts/detector/shared/color.mjs @@ -103,14 +103,22 @@ function extractColorFunctionTokens(value) { function parseGradientColors(bgImage) { if (!bgImage || !bgImage.includes('gradient')) return []; const colors = []; + const tokenSpans = []; + let from = 0; // Stops arrive in whatever syntax the author wrote and the browser kept. // A dark ground painted as `linear-gradient(oklch(...), oklch(...))` used // to read as a gradient with no stops at all. for (const token of extractColorFunctionTokens(bgImage)) { + const start = bgImage.indexOf(token, from); + if (start < 0) break; + tokenSpans.push({ start, end: start + token.length }); + from = start + token.length; const c = parseAnyColor(token); if (c) colors.push(c); } for (const m of bgImage.matchAll(/#([0-9a-f]{6}|[0-9a-f]{3})\b/gi)) { + // Nested hex inside color-mix is an ingredient, not a stop (issue #578). + if (tokenSpans.some(s => m.index >= s.start && m.index < s.end)) continue; const h = m[1]; if (h.length === 6) { colors.push({ r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16), a: 1 });