Update source code and remove runtime data from tracking

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>
This commit is contained in:
jjbruntonandClaude Opus 4.6 committed 2026-05-20 18:54:07 +01:00
1 parent e90213d1ff
commit dfa0580859
23 files changed
+5324 -347

No files matched your search

+249
View File
@@ -0,0 +1,249 @@
/**
* 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);