minor changes

This commit is contained in:
2025-11-25 21:22:56 +01:00
parent ac6f11cefa
commit 3ff25d3f7e

View File

@@ -2101,10 +2101,13 @@ function collectRegexMatches(regex, text, limit = 20) {
return Array.from(new Set(matches)).slice(0, limit);
}
function filterScorelines(candidates = []) {
function filterScorelines(candidates = [], sourceText = '') {
const filtered = [];
const lowerSource = typeof sourceText === 'string' ? sourceText.toLowerCase() : '';
for (const raw of candidates) {
const parts = raw.split(':').map((part) => part.trim());
const value = typeof raw === 'string' ? raw : (raw && raw.value) || '';
const index = typeof raw === 'string' ? -1 : (raw && typeof raw.index === 'number' ? raw.index : -1);
const parts = value.split(':').map((part) => part.trim());
if (parts.length !== 2) {
continue;
}
@@ -2118,6 +2121,17 @@ function filterScorelines(candidates = []) {
if (a > 15 || b > 15) {
continue;
}
if (index >= 0 && lowerSource) {
const contextStart = Math.max(0, index - 12);
const contextEnd = Math.min(lowerSource.length, index + value.length + 8);
const context = lowerSource.slice(contextStart, contextEnd);
const before = lowerSource.slice(Math.max(0, index - 6), index);
const hasTimeIndicatorBefore = /\bum\s*$/.test(before);
const hasTimeIndicatorAfter = /\buhr/.test(context);
if (hasTimeIndicatorBefore || hasTimeIndicatorAfter) {
continue;
}
}
filtered.push(`${a}:${b}`);
}
return filtered;
@@ -2184,8 +2198,12 @@ function evaluateSportsScore(text, moderationSettings = null) {
const hitDetails = [];
let score = 0;
const scorelineMatchesRaw = collectRegexMatches(/\b\d{1,2}\s*:\s*\d{1,2}\b/g, normalizedText);
const scorelineMatches = filterScorelines(scorelineMatchesRaw);
const scorelineMatchesRaw = Array.from(normalizedText.matchAll(/\b\d{1,2}\s*:\s*\d{1,2}\b/g))
.map((match) => ({
value: match[0],
index: typeof match.index === 'number' ? match.index : -1
}));
const scorelineMatches = filterScorelines(scorelineMatchesRaw, normalizedText);
applyWeight(scorelineMatches.length, weights.scoreline, 'Ergebnis', scorelineMatches);
const scoreEmojiMatches = collectRegexMatches(/[0-9]️⃣/g, normalizedText)
@@ -3409,6 +3427,283 @@ document.addEventListener('contextmenu', (event) => {
console.log('[FB Tracker] Context menu opened on:', contextMenuTarget);
}, true);
// Floating AI button on text selection
let selectionAIContainer = null;
let selectionAIButton = null;
let selectionAINoteButton = null;
let selectionAIRaf = null;
let selectionAIHideTimeout = null;
let selectionAIEnabledCached = null;
const clearSelectionAIHideTimeout = () => {
if (selectionAIHideTimeout) {
clearTimeout(selectionAIHideTimeout);
selectionAIHideTimeout = null;
}
};
const hideSelectionAIButton = () => {
clearSelectionAIHideTimeout();
if (selectionAIContainer) {
selectionAIContainer.style.display = 'none';
}
if (selectionAIButton) {
selectionAIButton.dataset.selectionText = '';
}
if (selectionAINoteButton) {
selectionAINoteButton.dataset.selectionText = '';
}
};
const ensureSelectionAIButton = () => {
if (selectionAIContainer && selectionAIContainer.isConnected && selectionAIButton && selectionAINoteButton) {
return selectionAIContainer;
}
const container = document.createElement('div');
container.style.cssText = `
position: fixed;
z-index: 2147483647;
display: none;
align-items: center;
gap: 8px;
pointer-events: auto;
`;
const noteButton = document.createElement('button');
noteButton.type = 'button';
noteButton.textContent = ' Zusatzinfo';
noteButton.title = 'Aktuelle Auswahl als Zusatzinfo speichern';
noteButton.style.cssText = `
padding: 7px 10px;
border-radius: 10px;
border: 1px solid #d1d5db;
background: #fff;
color: #111827;
font-weight: 700;
font-size: 12px;
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
cursor: pointer;
transition: transform 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
display: inline-flex;
align-items: center;
gap: 6px;
`;
noteButton.addEventListener('mouseenter', () => {
noteButton.style.transform = 'translateY(-1px)';
noteButton.style.boxShadow = '0 8px 18px rgba(0, 0, 0, 0.18)';
});
noteButton.addEventListener('mouseleave', () => {
noteButton.style.transform = 'translateY(0)';
noteButton.style.boxShadow = '0 6px 16px rgba(0, 0, 0, 0.12)';
});
noteButton.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
const selectedText = noteButton.dataset.selectionText || '';
if (!selectedText.trim()) {
showToast('Keine Textauswahl gefunden', 'error');
return;
}
const selection = window.getSelection();
const anchorNode = selection ? (selection.anchorNode || selection.focusNode) : null;
if (anchorNode && isSelectionInsideEditable(anchorNode)) {
showToast('Textauswahl aus Eingabefeldern kann nicht genutzt werden', 'error');
return;
}
const anchorElement = anchorNode && anchorNode.nodeType === Node.TEXT_NODE
? anchorNode.parentElement
: anchorNode;
const postContext = anchorElement ? ensurePrimaryPostElement(anchorElement) : null;
if (!postContext) {
showToast('Keinen zugehörigen Beitrag gefunden', 'error');
return;
}
const normalized = normalizeSelectionText(selectedText);
if (!normalized) {
showToast('Keine Textauswahl gefunden', 'error');
return;
}
postAdditionalNotes.set(postContext, normalized);
showToast('Auswahl als Zusatzinfo gesetzt', 'success');
});
const button = document.createElement('button');
button.type = 'button';
button.textContent = '✨ AI';
button.title = 'Auswahl mit AI beantworten';
button.style.cssText = `
padding: 8px 12px;
padding: 8px 12px;
border-radius: 999px;
border: none;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
font-weight: 700;
font-size: 13px;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.22);
cursor: pointer;
align-items: center;
gap: 6px;
transition: transform 120ms ease, box-shadow 120ms ease, opacity 120ms ease;
`;
button.addEventListener('mouseenter', () => {
button.style.transform = 'translateY(-1px) scale(1.02)';
button.style.boxShadow = '0 10px 22px rgba(0, 0, 0, 0.26)';
});
button.addEventListener('mouseleave', () => {
button.style.transform = 'translateY(0)';
button.style.boxShadow = '0 8px 20px rgba(0, 0, 0, 0.22)';
});
button.addEventListener('click', async (event) => {
event.preventDefault();
event.stopPropagation();
const selectedText = button.dataset.selectionText || '';
hideSelectionAIButton();
if (!selectedText.trim()) {
return;
}
const originalLabel = button.textContent;
button.textContent = '⏳ AI läuft...';
try {
await handleSelectionAIRequest(selectedText, () => {});
} finally {
button.textContent = originalLabel;
}
});
container.appendChild(noteButton);
container.appendChild(button);
document.body.appendChild(container);
selectionAIContainer = container;
selectionAIButton = button;
selectionAINoteButton = noteButton;
selectionAIButton = button;
return container;
};
const isSelectionInsideEditable = (node) => {
if (!node) {
return false;
}
if (node.nodeType === Node.ELEMENT_NODE) {
const el = node;
if (el.closest('input, textarea, [contenteditable="true"]')) {
return true;
}
}
if (node.parentElement && node.parentElement.closest('input, textarea, [contenteditable="true"]')) {
return true;
}
return false;
};
const positionSelectionAIButton = (rect) => {
if (!selectionAIContainer || !rect) {
return;
}
const viewportPadding = 8;
const containerWidth = selectionAIContainer.offsetWidth || 160;
let left = rect.right + 8;
let top = rect.top - (selectionAIContainer.offsetHeight || 40) - 8;
if (left + containerWidth + viewportPadding > window.innerWidth) {
left = Math.max(viewportPadding, rect.right - containerWidth - 8);
}
if (top < viewportPadding) {
top = rect.bottom + 8;
}
selectionAIContainer.style.left = `${Math.max(viewportPadding, left)}px`;
selectionAIContainer.style.top = `${Math.max(viewportPadding, top)}px`;
};
const updateSelectionAIButton = async () => {
clearSelectionAIHideTimeout();
if (selectionAIEnabledCached === null) {
try {
selectionAIEnabledCached = await isAIEnabled();
} catch (error) {
console.warn('[FB Tracker] AI enable check failed for selection button:', error);
selectionAIEnabledCached = false;
}
}
if (!selectionAIEnabledCached) {
hideSelectionAIButton();
return;
}
const selection = window.getSelection();
if (!selection || selection.isCollapsed) {
hideSelectionAIButton();
return;
}
const selectionText = (selection.toString() || '').trim();
if (!selectionText || selectionText.length > MAX_SELECTION_LENGTH) {
hideSelectionAIButton();
return;
}
const anchorNode = selection.anchorNode || selection.focusNode;
if (isSelectionInsideEditable(anchorNode)) {
hideSelectionAIButton();
return;
}
if (!selection.rangeCount) {
hideSelectionAIButton();
return;
}
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
if (!rect || (rect.width === 0 && rect.height === 0)) {
hideSelectionAIButton();
return;
}
const container = ensureSelectionAIButton();
if (!selectionAIButton || !selectionAINoteButton) {
hideSelectionAIButton();
return;
}
selectionAIButton.dataset.selectionText = selectionText;
selectionAINoteButton.dataset.selectionText = selectionText;
container.style.display = 'inline-flex';
positionSelectionAIButton(rect);
selectionAIHideTimeout = setTimeout(() => {
hideSelectionAIButton();
}, 8000);
};
const scheduleSelectionAIUpdate = () => {
if (selectionAIRaf) {
return;
}
selectionAIRaf = requestAnimationFrame(() => {
selectionAIRaf = null;
updateSelectionAIButton();
});
};
const initSelectionAIFloatingButton = () => {
document.addEventListener('selectionchange', scheduleSelectionAIUpdate, true);
document.addEventListener('mouseup', scheduleSelectionAIUpdate, true);
document.addEventListener('keyup', scheduleSelectionAIUpdate, true);
window.addEventListener('scroll', hideSelectionAIButton, true);
window.addEventListener('blur', hideSelectionAIButton, true);
};
initSelectionAIFloatingButton();
// Listen for manual reparse command
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message && message.type === 'generateSelectionAI') {
@@ -4793,6 +5088,40 @@ async function addAICommentButton(container, postElement) {
const buttonsRow = document.createElement('div');
buttonsRow.style.cssText = 'display: flex; gap: 8px; flex-wrap: wrap;';
const selectionButton = document.createElement('button');
selectionButton.type = 'button';
selectionButton.textContent = 'Auswahl als Zusatzinfo';
selectionButton.style.cssText = 'padding: 6px 10px; border-radius: 6px; border: 1px solid #ccd0d5; background: #fff; cursor: pointer; font-size: 12px; font-weight: 600; color: #1d2129;';
selectionButton.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
const selection = window.getSelection();
const anchorNode = selection ? (selection.anchorNode || selection.focusNode) : null;
if (anchorNode && isSelectionInsideEditable(anchorNode)) {
showToast('Textauswahl aus Eingabefeldern kann nicht genutzt werden', 'error');
return;
}
const context = resolvePostContext();
let selectedText = context ? getSelectedTextFromPost(context) : '';
if (!selectedText && selection) {
selectedText = normalizeSelectionText(selection.toString());
}
if (!selectedText && lastGlobalSelection.text && Date.now() - lastGlobalSelection.timestamp < LAST_SELECTION_MAX_AGE) {
selectedText = normalizeSelectionText(lastGlobalSelection.text);
}
if (!selectedText) {
showToast('Keine Textauswahl gefunden', 'error');
return;
}
setAdditionalNote(selectedText);
showToast('Auswahl als Zusatzinfo gesetzt', 'success');
});
buttonsRow.appendChild(selectionButton);
const editButton = document.createElement('button');
editButton.type = 'button';
editButton.textContent = 'Zusatzinfo bearbeiten';