(async () => {
const THREAD_CONTAINER_SELECTOR = 'div[data-sidebar-thread-id]';
const COMMENT_ROW_SELECTOR = 'div[data-test-id="commentRow"]';
const QUOTED_TEXT_SELECTOR = 'div.border-left-medium';
const AUTHOR_SELECTOR = 'span.font-weight-semibold.ellipsis.shade-00';
const TIMESTAMP_SELECTOR = 'span.font-size-normal.ellipsis.shade-30';
const COMMENT_BODY_SELECTOR = 'div[data-test-id="editCommentContent"]';
const findShowMoreElement = (threadElement) => {
const spans = threadElement.querySelectorAll('span.padding-left-40');
for (const span of spans) {
if (span.textContent.match(/Show \d+ more comment/)) {
return span;
}
}
return null;
};
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const EXPAND_DELAY_MS = 700;
console.clear();
console.log("š Starting Slite Comment Extractor (Sequential Mode)...");
const threadContainers = document.querySelectorAll(THREAD_CONTAINER_SELECTOR);
if (threadContainers.length === 0) {
console.warn("š« No comment threads found. Please ensure:\n1. The comments sidebar is open on the Slite page.\n2. The script's CSS selectors are up-to-date with Slite's current website structure.");
return;
}
console.log(`š Found ${threadContainers.length} comment thread(s). Processing one by one.`);
for (const [index, thread] of threadContainers.entries()) {
const threadNumber = index + 1;
console.log(`\n\n\n--- [ ${threadNumber}/${threadContainers.length} ] --- PROCESSING THREAD ---`);
thread.scrollIntoView({ behavior: 'smooth', block: 'center' });
await sleep(500);
let showMoreElement;
let expansionAttempts = 0;
const MAX_ATTEMPTS = 20;
while ((showMoreElement = findShowMoreElement(thread)) && expansionAttempts < MAX_ATTEMPTS) {
console.log(` [Thread ${threadNumber}] Expanding hidden replies...`);
showMoreElement.click();
await sleep(EXPAND_DELAY_MS);
expansionAttempts++;
}
console.log(` [Thread ${threadNumber}] ā
Expansion complete.`);
const commentRows = thread.querySelectorAll(COMMENT_ROW_SELECTOR);
let threadOutput = `<!-- ========== THREAD ${threadNumber} ========== -->\n\n`;
commentRows.forEach((row, rowIndex) => {
const authorEl = row.querySelector(AUTHOR_SELECTOR);
const timestampEl = row.querySelector(TIMESTAMP_SELECTOR);
const bodyEl = row.querySelector(COMMENT_BODY_SELECTOR);
const quotedEl = row.querySelector(QUOTED_TEXT_SELECTOR);
const author = authorEl ? authorEl.textContent.trim() : 'Unknown Author';
const timestamp = timestampEl ? timestampEl.textContent.trim() : 'Unknown Timestamp';
const body = bodyEl ? bodyEl.textContent.trim() : '[No content]';
threadOutput += `š¤ **${author}** (${timestamp})\n`;
if (quotedEl) {
const quotedText = quotedEl.textContent.trim().replace(/\s+/g, ' ');
threadOutput += `> š¬ _Replying to: "${quotedText}"_\n`;
}
threadOutput += `${body}\n`;
if (rowIndex < commentRows.length - 1) {
threadOutput += `\n----\n\n`;
}
});
threadOutput += `\n<!-- ========== END THREAD ${threadNumber} ========== -->`;
console.log(threadOutput);
}
console.log("\n\n\nš --- All threads have been processed and printed above! --- ⨠");
})();