Basic modifications to Lewdcorner, especially latest updates
// ==UserScript==
// @name LewdCorner Highlight Helper
// @namespace http://scizzer12.me
// @version 2026-07-28.1
// @description Basic modifications to Lewdcorner, especially latest updates
// @author scizzer12
// @match https://lewdcorner.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=lewdcorner.com
// @run-at document-start
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// Styles
GM_addStyle(`
.lcCardMediaAction.is-active[title] {
background: #f29bf9;
color: #000;
border-color: #f49def;
}
.lcCardMedia:hover .lcCardOverlay {
opacity: 1 !important;
}
.hated:not(:hover) {
opacity: 25%;
}
.lcCard--abandoned {
border: 1px solid red;
}
.lcCard--abandoned:hover {
opacity: 1;
}
.lcCardHoverDetails .lcCardHoverTags span {
background-color: black;
}`);
// Break closure and inject script changes
if (window.location.href.includes('latest-updates')) {
const observer = new MutationObserver((mutations) => {
mutations.forEach(m => m.addedNodes.forEach(node => {
if ( node.tagName === "SCRIPT" && node.textContent.substring(18,26) == 'var boot') {
node.textContent = node.textContent.replace( // Expose data
'var settings = readSettings();',
'var settings = readSettings();\n' +
'window.lcCtx = (prop) => eval(prop);'
).replace( // Bookmark note
"class=\"lcCardMediaAction' + (item.is_bookmarked",
"' + (item.bookmark_note ? 'title=\"' + item.bookmark_note + '\" ' : '') + 'class=\"lcCardMediaAction' + (item.is_bookmarked"
).replace( // Update image cache
"function enableManualMode()",
"card.refreshImages = () => { images = decodeImages(image.getAttribute('data-images')) }\n\n" +
"function enableManualMode()"
).replace( // Real image fallback
/image\.addEventListener\('error'[\s\S]+?}\);/, ""
)/*.replace( // pill click
/state\.tagsInc = state\.tagsInc\.concat\(\[tag\]\)\.slice\(0, 10\);\s*renderSelectedTags\('inc'\);\s*renderTagList\('inc'\);/,
"const type = button.classList.contains('negative') != event.shiftKey ? 'exc' : 'inc';" +
"const tagList = 'tags' + (type == 'inc' ? 'Inc' : 'Exc');" +
"state[tagList] = state[tagList].concat([tag]).slice(0, 10);" +
"renderSelectedTags(type);" +
"renderTagList(type);"
)*/;
// Options menu
dqs('#lcLuTabSaved').insertAdjacentHTML('beforebegin', '<button type="button" class="lcModalNavBtn" id="lcLuTabHighlight" data-pane="highlight" role="tab" aria-selected="false" aria-controls="lcLuPaneHighlight" tabindex="-1">Tag Highlights</button>');
dqs('#lcLuPaneSaved').insertAdjacentHTML(
'beforebegin',
'<div class="lcModalPane" id="lcLuPaneHighlight" data-pane-content="highlight" role="tabpanel" aria-labelledby="lcLuTabHighlight">' +
' <div class="lcModalSection">' +
' <div class="lcModalSectionTitle">Tag Highlights</div>' +
' <p class="lcModalSectionSub">Control how important tags are displayed.</p>' +
' <div class="lcSettingsList">' +
' <div class="lcSettingsRow"><div class="lcSettingsRowLabel">Liked<span class="lcSettingsRowHint">ordered,comma separated,list</span></div><input type="text" name="likedTags" class="lcClassicInput" placeholder="Positive tags" autocomplete="off"></div>' +
' <div class="lcSettingsRow"><div class="lcSettingsRowLabel">Disliked<span class="lcSettingsRowHint">ordered,comma separated,list</span></div><input type="text" name="dislikedTags" class="lcClassicInput" placeholder="Negative tags" autocomplete="off"></div>' +
' <div class="lcSettingsRow"><div class="lcSettingsRowLabel">Hated<span class="lcSettingsRowHint">ordered,comma separated,list</span></div><input type="text" name="hatedTags" class="lcClassicInput" placeholder="Dealbreaker tags" autocomplete="off"></div>' +
' </div>' +
' </div>' +
'</div>'
);
observer.disconnect();
}
}));
});
observer.observe(document.documentElement, { childList: true, subtree: true });
}
// Tooling
const dqs = document.querySelector.bind(document),
dqsa = document.querySelectorAll.bind(document),
dqsaA = (a) => [...document.querySelectorAll(a)];
function live(sel, event, callback, context = document) {
context.addEventListener(event, (e) => {
const el = e.target || e.srcElement,
found = el.closest?.(sel);
if (found) callback.call(found, e);
});
}
async function lazyLoad(test, maxTries = 100) {
return new Promise((res, rej) => {
if (typeof test != 'function')
return (new MutationObserver((m,o) => { o.disconnect(); return res(m);}).observe(test, {childList: true}));
let result = test();
if (!result) {
let tries = 0;
const poll = setInterval(() => {
result = test();
if (result) {
clearInterval(poll);
return res(result);
}
else if (tries > maxTries) {
clearInterval(poll);
return rej();
}
else tries++;
}, 200);
}
else res(result);
});
}
let likedTags = GM_getValue('likedTags', []);
let dislikedTags = GM_getValue('dislikedTags', []);
let hatedTags = GM_getValue('hatedTags', []);
// Backup image thumbnails
const needScrape = new Set();
let scrapeCache = localStorage.getItem('scrapeCache');
let fullRefresh = false;
if (scrapeCache) scrapeCache = new Map(JSON.parse(scrapeCache).map(m => [m[0], new Map(m[1])]));
else scrapeCache = new Map();
let scrapeRunning = false;
function queueScrape(link) {
needScrape.add(link);
if (!scrapeRunning) { scrapeRunning = true; scrape(); }
}
async function scrape() {
const url = [...needScrape][0];
let matches,
queued = scrapeCache.has(url);
const result = lcCtx('result').items.find(r => r.link === url),
img = dqs(`.lcCard[data-card-id="${result.id}"] .lcCardImage`);
if (queued) matches = scrapeCache.get(url);
else {
const res = await fetch(url);
const text = await res.text();
matches = new Map([...text.matchAll(/<a href="([^"]+)"\s*target="_blank" class="js-lbImage"><img src="([^"]+)"/g)].map(m => m.slice(1)));
scrapeCache.set(url, matches);
}
if (!matches.size) {
if (result) Object.assign(result, { dead: true, images: '', image: '' });
fullRefresh = true;
}
else if (result) {
if (result.images.length) result.images = result.images.map(i => matches.get(i)).filter(i => i);
else result.images = [...matches.values()];
result.image = matches.get(result.image) || result.images[0] || "";
if (img) {
img.src = result.image;
img.dataset.images = escape(JSON.stringify(result.images));
}
else fullRefresh = true;
const card = dqs(`.lcCard[data-card-id="${result.id}"]`);
card.refreshImages?.();
card.style.border = "1px solid pink";
}
needScrape.delete(url);
if (needScrape.size) scrape();
else {
scrapeRunning = false;
localStorage.setItem('scrapeCache', JSON.stringify([...scrapeCache.entries()].map(m => [m[0], [...m[1].entries()]])));
if (fullRefresh) {
fullRefresh = false;
lcCtx('render')();
}
}
}
function pageLoad(e) {
const location = e?.destination?.url || document.location.href;
if (location.includes('/threads/')) {
// Tag highlights in threads
dqsa('.p-description .js-tagList a').forEach(t => {
const text = t.textContent.trim();
if (likedTags.includes(text)) t.style.background = 'darkgreen';
else if (dislikedTags.includes(text)) t.setAttribute('style', 'background: thistle; color: black;');
else if (hatedTags.includes(text)) t.style.background = 'darkred';
});
}
else if (location.includes('latest-updates')) {
// Clickable tags, shift click to invert
live('.lcCardHoverTags span', 'click', e => {
var tag = e.target.textContent.trim() || '';
if (tag && lcCtx('state').tagsInc.indexOf(tag) === -1) {
const type = e.target.classList.contains('negative') != event.shiftKey ? 'exc' : 'inc';
const tagList = 'tags' + (type == 'inc' ? 'Inc' : 'Exc');
lcCtx('state')[tagList] = lcCtx('state')[tagList].concat([tag]).slice(0, 10);
lcCtx('renderSelectedTags')(type);
lcCtx('renderTagList')(type);
lcCtx('refresh')();
}
});
// Option menu
dqs('#lcLuPaneHighlight').querySelectorAll('input').forEach(i => {
const name = i.name.replace(/\W/g,'');
if (name === 'likedTags') i.value = likedTags.join(', ');
else if (name === 'dislikedTags') i.value = dislikedTags.join(', ');
else if (name === 'hatedTags') i.value = hatedTags.join(', ');
i.addEventListener('change', e => {
const values = i.value.trim().toLowerCase().split(/\s*,\s*/);
GM_setValue(name, values);
eval(`${name} = values`);
i.value = values.join(', ');
lcCtx('refresh')();
});
});
const processGrid = () => {
const firstCard = dqs('.lcClassicGrid article.lcCard'),
items = lcCtx('result').items;
if (!firstCard || firstCard.filtered) return;
else firstCard.filtered = true;
dqsa('.lcClassicGrid article.lcCard').forEach((c, i) => {
const res = items[i] || items.find(i => i.id == c.dataset['card-id']);
const bookmarked = c.querySelector('.lcCardTagOverflowWrap+.lcCardMediaAction.is-active');
if (bookmarked) c.style.border = "1px solid " + getComputedStyle(bookmarked).backgroundColor;
const shortTagBox = c.querySelector('.lcCardRestingTagList');
const shortTags = [...c.querySelectorAll('.lcCardRestingTagList>button.lcCardRestingTag')];
let allTags = c.querySelectorAll('button.lcCardRestingTag');
const previewTags = new Map([...c.querySelectorAll('.lcCardHoverTags span')].map(s => [s.textContent.trim(), s]));
if (!allTags.length) allTags = shortTags;
const imgContainer = c.querySelector('.lcCardImage');
const link = c.querySelector('.lcCardHoverMediaLink').href;
if (scrapeCache.get(link)) c.style.border = "1px solid pink";
if (!imgContainer && !res.dead) queueScrape(link);
else imgContainer?.addEventListener('error', e => {e.preventDefault(); e.stopPropagation(); queueScrape(link); }, { once: true });
let score = 0;
// Tag processing
allTags.forEach(t => {
const text = t.textContent.trim().replace(/-/g, ' ');
const preview = previewTags.get(t.textContent.trim());
if (likedTags.includes(text)) t.style.background = 'darkgreen';
else if (dislikedTags.includes(text)) { t.setAttribute('style', 'background: thistle; color: black;'); t.classList.add('negative'); }
else if (hatedTags.includes(text)) { t.style.background = 'darkred'; t.classList.add('negative'); }
else return;
const exists = shortTags.find(s => s.textContent.trim().replace(/-/g, ' ') === text);
if (exists) { exists.style.background = t.style.background; exists.style.color = t.style.color; }
else {
const normalTag = shortTags.findLastIndex(s => !s.style.background);
if (normalTag >= 0) shortTags.splice(normalTag, 1)[0].remove();
}
shortTags[0].insertAdjacentElement('beforebegin', exists ? exists : t);
shortTags.unshift(t);
if (t.style.background == 'darkred') c.classList.add('hated');
preview.setAttribute('style', shortTags[0].getAttribute('style'));
if (t.classList.contains('negative')) preview.classList.add('negative');
});
});
lazyLoad(dqs('#lcLuGrid')).then(processGrid);
};
processGrid();
}
}
navigation.addEventListener('navigate', pageLoad);
document.addEventListener('DOMContentLoaded', pageLoad);
})();