Skip to content

Commit 6b85aa3

Browse files
Add selectorContains discriminator for compound CSS selectors
The Metadata-module__secondary prefix has two stylesheet variants with identical CSS properties but different compound selectors. The scanner's discriminated-key path only collected simple selectors, so both compounds were skipped and the wrong variant was picked by first-match-wins. Extend discriminators to support selector text matching and collect candidates from compound selectors. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 114a733 commit 6b85aa3

2 files changed

Lines changed: 20 additions & 13 deletions

File tree

extension/assets/content-issues-list.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ registerCssClasses([
8787
'MetadataContainer-module__container',
8888
'IssueItem-module__ListItem_0',
8989
'Metadata-module__metadata',
90-
'Metadata-module__secondary',
90+
['Metadata-module__secondary', 'selectorContains', 'Metadata-module__metadata'],
9191
'Metadata-module__alignRight',
9292
'IssueItemMetadata-module__ListItemMetadata_0',
9393
'IssueItem-module__commentCountContainer',

extension/assets/shared.js

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,24 @@ if (typeof CSS_CLASSES !== 'undefined') {
4848
}
4949
// Use var (not const) so the second execution doesn't throw a SyntaxError
5050
var CSS_CLASSES = CSS_CLASSES || new Map();
51-
// Discriminators for ambiguous prefixes: prefix -> { property, value }
51+
// Discriminators for ambiguous prefixes: prefix -> { type, property, value }
52+
// Types: 'css' (default) checks rule.style.getPropertyValue(property) === value
53+
// 'selector' checks rule.selectorText.includes(value)
5254
var CSS_DISCRIMINATORS = CSS_DISCRIMINATORS || new Map();
5355

5456
// Register CSS class prefixes for discovery.
55-
// Each entry is either a string (prefix) or a tuple [prefix, property, value]
56-
// where the property/value pair disambiguates when multiple stylesheet rules
57-
// share the same prefix (e.g., different React components with same module name).
57+
// Each entry is either a string (prefix) or a tuple:
58+
// [prefix, property, value] — CSS property discriminator (checks rule.style)
59+
// [prefix, 'selectorContains', substring] — selector discriminator (checks rule.selectorText)
5860
function registerCssClasses(keys) {
5961
for (const entry of keys) {
6062
const key = Array.isArray(entry) ? entry[0] : entry;
6163
if (!CSS_CLASSES.has(key)) {
6264
CSS_CLASSES.set(key, key);
6365
}
6466
if (Array.isArray(entry)) {
65-
CSS_DISCRIMINATORS.set(key, { property: entry[1], value: entry[2] });
67+
const type = entry[1] === 'selectorContains' ? 'selector' : 'css';
68+
CSS_DISCRIMINATORS.set(key, { type, property: entry[1], value: entry[2] });
6669
}
6770
}
6871
}
@@ -118,9 +121,14 @@ function discoverCssClasses() {
118121

119122
// Resolve discriminated keys by picking the candidate matching the discriminator
120123
for (const key of discriminated) {
121-
const { property, value } = CSS_DISCRIMINATORS.get(key);
124+
const disc = CSS_DISCRIMINATORS.get(key);
122125
const matches = candidates.get(key);
123-
const winner = matches.find(c => c.rule.style.getPropertyValue(property) === value);
126+
let winner;
127+
if (disc.type === 'selector') {
128+
winner = matches.find(c => c.selectorText.includes(disc.value));
129+
} else {
130+
winner = matches.find(c => c.rule.style.getPropertyValue(disc.property) === disc.value);
131+
}
124132
if (winner) {
125133
CSS_CLASSES.set(key, winner.className);
126134
pending.delete(key);
@@ -130,7 +138,7 @@ function discoverCssClasses() {
130138
CSS_CLASSES.set(key, matches[0].className);
131139
pending.delete(key);
132140
discovered++;
133-
console.warn(`[Bookmarked] ${key}: discriminator ${property}=${value} not found, using first match`);
141+
console.log(`[Bookmarked] ${key}: discriminator not found, using first match`);
134142
}
135143
}
136144

@@ -167,13 +175,12 @@ function scanRules(rules, simple, discriminated, keyRegexes, candidates) {
167175
}
168176
}
169177

170-
// Check discriminated keys (collect all candidates)
178+
// Check discriminated keys (collect all candidates, including compound selectors)
171179
for (const key of discriminated) {
172180
if (!rule.selectorText.includes(key)) continue;
173181
const match = rule.selectorText.match(keyRegexes.get(key));
174-
if (match && rule.selectorText === match[0]) {
175-
// Only consider rules where the selector IS the class (not compound selectors)
176-
candidates.get(key).push({ className: match[0].slice(1), rule });
182+
if (match) {
183+
candidates.get(key).push({ className: match[0].slice(1), selectorText: rule.selectorText, rule });
177184
}
178185
}
179186
}

0 commit comments

Comments
 (0)