diff --git a/Anson-Projects/badfriends/index.html b/Anson-Projects/badfriends/index.html index 6268a312a..9d8e85f3f 100644 --- a/Anson-Projects/badfriends/index.html +++ b/Anson-Projects/badfriends/index.html @@ -65,8 +65,11 @@

Analyzing your follows...

-

-

+
+
diff --git a/Anson-Projects/badfriends/src/main.js b/Anson-Projects/badfriends/src/main.js index fd8fdd5da..51e01bcfb 100644 --- a/Anson-Projects/badfriends/src/main.js +++ b/Anson-Projects/badfriends/src/main.js @@ -43,6 +43,7 @@ const elements = { loginForm: document.getElementById('login-form'), handleInput: document.getElementById('handle-input'), loadingMessage: document.getElementById('loading-message'), + loadingLog: document.getElementById('loading-log'), loadingDetail: document.getElementById('loading-detail'), loadingEta: document.getElementById('loading-eta'), userHandle: document.getElementById('user-handle'), @@ -195,7 +196,8 @@ async function startAnalysis() { elements.userHandle.textContent = `@${profile.handle}`; // Run analysis - updateLoadingMessage('Starting analysis...'); + updateLoadingMessage('Analyzing your follows...'); + clearLoadingLog(); // Reset ETA tracker, unfollowed accounts, and deep scan progress etaTracker = { @@ -212,7 +214,7 @@ async function startAnalysis() { const results = await analyzeFollows({ onStatus: (message) => { - updateLoadingMessage(message); + addLogEntry(message); // Parse deep scan progress from status messages // Format: "Deep scan: X% (Y/Z accounts)" @@ -801,13 +803,74 @@ function showScreen(screenName) { } } +// Track the last log message to avoid duplicates +let lastLogMessage = ''; + /** - * Update loading message + * Update loading message (the main header) */ function updateLoadingMessage(message) { elements.loadingMessage.textContent = message; } +/** + * Clear the loading log + */ +function clearLoadingLog() { + if (elements.loadingLog) { + elements.loadingLog.innerHTML = ''; + lastLogMessage = ''; + } +} + +/** + * Add a message to the loading log + * Messages fade as new ones are added + */ +function addLogEntry(message) { + if (!elements.loadingLog || !message) return; + + // Skip duplicate consecutive messages (e.g., repeated "Fetching posts...") + // But allow updates with counts (e.g., "Fetching posts... 10 of 100") + const baseMessage = message.replace(/\d+/g, '#'); + const lastBaseMessage = lastLogMessage.replace(/\d+/g, '#'); + + if (baseMessage === lastBaseMessage) { + // Update the last entry instead of adding a new one + const lastEntry = elements.loadingLog.querySelector('.log-entry.current'); + if (lastEntry) { + lastEntry.textContent = message; + lastLogMessage = message; + return; + } + } + + lastLogMessage = message; + + // Fade previous "current" entries + const currentEntries = elements.loadingLog.querySelectorAll('.log-entry.current'); + currentEntries.forEach(entry => { + entry.classList.remove('current'); + entry.classList.add('fade'); + }); + + // Create new entry + const entry = document.createElement('div'); + entry.className = 'log-entry current'; + entry.textContent = message; + + elements.loadingLog.appendChild(entry); + + // Scroll to bottom + elements.loadingLog.scrollTop = elements.loadingLog.scrollHeight; + + // Limit entries to prevent memory issues (keep last 20) + const entries = elements.loadingLog.querySelectorAll('.log-entry'); + if (entries.length > 20) { + entries[0].remove(); + } +} + /** * Update loading detail */ diff --git a/Anson-Projects/badfriends/src/styles.css b/Anson-Projects/badfriends/src/styles.css index 6bf84b29e..b9bf92601 100644 --- a/Anson-Projects/badfriends/src/styles.css +++ b/Anson-Projects/badfriends/src/styles.css @@ -204,6 +204,53 @@ input[type="text"] { font-size: 0.95rem; } +/* Loading Log */ +.loading-log { + max-height: 200px; + overflow-y: auto; + margin: 1.5rem 0; + text-align: left; + font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace; + font-size: 0.875rem; + padding: 1rem; + background: var(--bg-secondary); + border-radius: 8px; + min-height: 100px; +} + +.loading-log:empty { + display: none; +} + +.log-entry { + padding: 0.25rem 0; + color: var(--text-secondary); + opacity: 1; + transition: opacity 0.5s ease; + display: flex; + align-items: flex-start; + gap: 0.5rem; +} + +.log-entry::before { + content: "→"; + color: var(--primary-color); + flex-shrink: 0; +} + +.log-entry.fade { + opacity: 0.4; +} + +.log-entry.current { + color: var(--text-primary); + font-weight: 500; +} + +.loading-footer { + margin-top: 1rem; +} + /* Results Screen */ .controls { background: var(--card-background);