Add gitignore rules for browser session data, SQLite databases, media downloads, and build artifacts. Remove previously tracked runtime files from the repository. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
250 lines
8.7 KiB
TypeScript
250 lines
8.7 KiB
TypeScript
/**
|
|
* Diagnostic script: inspect the actual DOM structure of Reddit chat
|
|
* to understand how media images are rendered.
|
|
*
|
|
* Usage: npx tsx scripts/inspect-chat-dom.ts
|
|
*/
|
|
import { chromium } from 'playwright';
|
|
import path from 'path';
|
|
|
|
async function main() {
|
|
const sessionDir = '/tmp/reddit-session-copy';
|
|
|
|
console.log('Launching browser with session copy...');
|
|
const context = await chromium.launchPersistentContext(sessionDir, {
|
|
headless: false,
|
|
viewport: { width: 1280, height: 720 },
|
|
args: ['--no-sandbox', '--disable-blink-features=AutomationControlled'],
|
|
});
|
|
|
|
const page = await context.newPage();
|
|
await page.goto('https://chat.reddit.com/', {
|
|
waitUntil: 'domcontentloaded',
|
|
timeout: 30000,
|
|
});
|
|
await page.waitForTimeout(5000);
|
|
|
|
console.log('\n=== Finding chat rooms ===');
|
|
|
|
// Find the first chat room and click into it
|
|
const firstChatUrl = await page.evaluate(`
|
|
(function() {
|
|
var rsApp = document.querySelector('rs-app');
|
|
if (!rsApp || !rsApp.shadowRoot) return null;
|
|
|
|
// Look for room links
|
|
function findLinks(root) {
|
|
if (!root) return [];
|
|
var links = Array.from(root.querySelectorAll('a[href*="/room/"]'));
|
|
var allElements = root.querySelectorAll('*');
|
|
for (var i = 0; i < allElements.length; i++) {
|
|
if (allElements[i].shadowRoot) {
|
|
links = links.concat(findLinks(allElements[i].shadowRoot));
|
|
}
|
|
}
|
|
return links;
|
|
}
|
|
|
|
var links = findLinks(rsApp.shadowRoot);
|
|
if (links.length === 0) return null;
|
|
return links[0].href;
|
|
})()
|
|
`);
|
|
|
|
console.log('First chat URL:', firstChatUrl);
|
|
|
|
if (firstChatUrl) {
|
|
await page.goto(firstChatUrl, { waitUntil: 'domcontentloaded', timeout: 15000 });
|
|
await page.waitForTimeout(3000);
|
|
}
|
|
|
|
console.log('\n=== Inspecting message area DOM structure ===');
|
|
|
|
const domInfo = await page.evaluate(`
|
|
(function() {
|
|
var results = {
|
|
rsApp: false,
|
|
rsRoom: false,
|
|
rsRoomShadow: false,
|
|
customElements: [],
|
|
allImages: [],
|
|
messageStructure: [],
|
|
shadowRootChain: [],
|
|
};
|
|
|
|
var rsApp = document.querySelector('rs-app');
|
|
if (!rsApp || !rsApp.shadowRoot) {
|
|
results.error = 'No rs-app shadow root found';
|
|
return results;
|
|
}
|
|
results.rsApp = true;
|
|
|
|
var rsRoom = rsApp.shadowRoot.querySelector('rs-room');
|
|
if (!rsRoom) {
|
|
results.error = 'No rs-room found';
|
|
return results;
|
|
}
|
|
results.rsRoom = true;
|
|
results.rsRoomShadow = !!rsRoom.shadowRoot;
|
|
|
|
// Find ALL custom elements and their shadow roots recursively
|
|
function mapShadowDom(root, depth, path) {
|
|
if (!root || depth > 8) return;
|
|
var allEls = root.querySelectorAll('*');
|
|
for (var i = 0; i < allEls.length; i++) {
|
|
var el = allEls[i];
|
|
var tag = el.tagName.toLowerCase();
|
|
if (tag.includes('-') || el.shadowRoot) {
|
|
var entry = {
|
|
tag: tag,
|
|
depth: depth,
|
|
path: path + ' > ' + tag,
|
|
hasShadow: !!el.shadowRoot,
|
|
childCount: el.childElementCount,
|
|
className: (el.className || '').toString().slice(0, 100),
|
|
};
|
|
results.customElements.push(entry);
|
|
if (el.shadowRoot) {
|
|
mapShadowDom(el.shadowRoot, depth + 1, path + ' > ' + tag + ' [shadow]');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var searchRoot = rsRoom.shadowRoot || rsRoom;
|
|
mapShadowDom(searchRoot, 0, 'rs-room');
|
|
|
|
// Find ALL images recursively through shadow DOMs
|
|
function findAllImages(root, depth, path) {
|
|
if (!root || depth > 8) return;
|
|
var imgs = root.querySelectorAll('img');
|
|
for (var i = 0; i < imgs.length; i++) {
|
|
var img = imgs[i];
|
|
results.allImages.push({
|
|
src: (img.getAttribute('src') || '').slice(0, 200),
|
|
alt: img.getAttribute('alt') || '',
|
|
width: img.width,
|
|
height: img.height,
|
|
naturalWidth: img.naturalWidth,
|
|
naturalHeight: img.naturalHeight,
|
|
className: (img.className || '').toString().slice(0, 100),
|
|
parentTag: img.parentElement ? img.parentElement.tagName.toLowerCase() : 'none',
|
|
parentClass: img.parentElement ? (img.parentElement.className || '').toString().slice(0, 100) : '',
|
|
grandparentTag: img.parentElement && img.parentElement.parentElement ? img.parentElement.parentElement.tagName.toLowerCase() : 'none',
|
|
path: path,
|
|
depth: depth,
|
|
});
|
|
}
|
|
var allEls = root.querySelectorAll('*');
|
|
for (var j = 0; j < allEls.length; j++) {
|
|
if (allEls[j].shadowRoot) {
|
|
findAllImages(allEls[j].shadowRoot, depth + 1, path + ' > ' + allEls[j].tagName.toLowerCase() + '[shadow]');
|
|
}
|
|
}
|
|
}
|
|
|
|
findAllImages(searchRoot, 0, 'rs-room');
|
|
|
|
// Try to understand message structure: look for repeated patterns
|
|
// Walk the shadow DOM and find elements that look like messages
|
|
function findMessageElements(root, depth, path) {
|
|
if (!root || depth > 8) return;
|
|
var allEls = root.querySelectorAll('*');
|
|
for (var i = 0; i < allEls.length; i++) {
|
|
var el = allEls[i];
|
|
var tag = el.tagName.toLowerCase();
|
|
var cls = (el.className || '').toString().toLowerCase();
|
|
var text = (el.textContent || '').trim();
|
|
|
|
// Look for elements that contain message-like content
|
|
if (tag.includes('message') || cls.includes('message') ||
|
|
tag.includes('msg') || cls.includes('msg') ||
|
|
tag.includes('chat') || cls.includes('bubble')) {
|
|
results.messageStructure.push({
|
|
tag: tag,
|
|
className: cls.slice(0, 150),
|
|
path: path,
|
|
depth: depth,
|
|
textSnippet: text.slice(0, 100),
|
|
childCount: el.childElementCount,
|
|
hasImages: el.querySelectorAll('img').length,
|
|
innerHTML: el.innerHTML.slice(0, 300),
|
|
});
|
|
}
|
|
|
|
if (el.shadowRoot) {
|
|
findMessageElements(el.shadowRoot, depth + 1, path + ' > ' + tag + '[shadow]');
|
|
}
|
|
}
|
|
}
|
|
|
|
findMessageElements(searchRoot, 0, 'rs-room');
|
|
|
|
return results;
|
|
})()
|
|
`);
|
|
|
|
console.log('\n--- Shadow DOM Structure ---');
|
|
console.log('rs-app:', domInfo.rsApp);
|
|
console.log('rs-room:', domInfo.rsRoom);
|
|
console.log('rs-room has shadow:', domInfo.rsRoomShadow);
|
|
if (domInfo.error) console.log('ERROR:', domInfo.error);
|
|
|
|
console.log('\n--- Custom Elements with Shadow Roots ---');
|
|
for (const el of domInfo.customElements || []) {
|
|
console.log(`${' '.repeat(el.depth)}${el.tag} (shadow: ${el.hasShadow}, children: ${el.childCount}) ${el.className ? '[class: ' + el.className + ']' : ''}`);
|
|
}
|
|
|
|
console.log('\n--- All Images Found ---');
|
|
for (const img of domInfo.allImages || []) {
|
|
console.log(` [depth=${img.depth}] ${img.src}`);
|
|
console.log(` size: ${img.width}x${img.height} (natural: ${img.naturalWidth}x${img.naturalHeight})`);
|
|
console.log(` class: ${img.className}`);
|
|
console.log(` parent: <${img.parentTag} class="${img.parentClass}">`);
|
|
console.log(` path: ${img.path}`);
|
|
console.log('');
|
|
}
|
|
|
|
console.log('\n--- Message-like Elements ---');
|
|
for (const msg of domInfo.messageStructure || []) {
|
|
console.log(` [depth=${msg.depth}] <${msg.tag}> images=${msg.hasImages}`);
|
|
console.log(` class: ${msg.className}`);
|
|
console.log(` text: ${msg.textSnippet}`);
|
|
console.log(` path: ${msg.path}`);
|
|
console.log(` html: ${msg.innerHTML}`);
|
|
console.log('');
|
|
}
|
|
|
|
// Also dump a broader view - find what's directly inside rs-room shadow
|
|
console.log('\n=== Direct children of message area ===');
|
|
const directChildren = await page.evaluate(`
|
|
(function() {
|
|
var rsApp = document.querySelector('rs-app');
|
|
if (!rsApp || !rsApp.shadowRoot) return 'no rs-app';
|
|
var rsRoom = rsApp.shadowRoot.querySelector('rs-room');
|
|
if (!rsRoom) return 'no rs-room';
|
|
var root = rsRoom.shadowRoot || rsRoom;
|
|
|
|
var children = [];
|
|
for (var i = 0; i < root.children.length; i++) {
|
|
var child = root.children[i];
|
|
children.push({
|
|
tag: child.tagName.toLowerCase(),
|
|
className: (child.className || '').toString().slice(0, 200),
|
|
id: child.id || '',
|
|
childCount: child.childElementCount,
|
|
hasShadow: !!child.shadowRoot,
|
|
textSnippet: (child.textContent || '').trim().slice(0, 150),
|
|
});
|
|
}
|
|
return children;
|
|
})()
|
|
`);
|
|
console.log(JSON.stringify(directChildren, null, 2));
|
|
|
|
console.log('\nDone. Closing browser...');
|
|
await context.close();
|
|
}
|
|
|
|
main().catch(console.error);
|