Functional scraping of the project page sans files.txt

This commit is contained in:
2025-07-17 11:10:13 -04:00
parent 111e3ee048
commit fbede823e5
2 changed files with 111 additions and 5 deletions

View File

@ -2,7 +2,7 @@ browser.browserAction.onClicked.addListener((tab) => {
console.log('Extension clicked - triggering artifact collection.');
// Check if we're on the correct domain and path
if (!tab.url.startsWith('https://claude.ai/chat/')) {
if (!tab.url.startsWith('https://claude.ai/chat') && !tab.url.startsWith('https://claude.ai/project')) {
console.log('Not on claude.ai chat page');
return;
}

View File

@ -234,18 +234,124 @@ const ChatArtifactScraper = {
};
const ProjectArtifactScraper = {
getArtifactListItems() {
const gridContainer = document.querySelector('ul.grid');
const artifactHash = {};
if (!gridContainer) {
return artifactHash;
}
gridContainer.querySelectorAll('h3').forEach((titleElement) => {
const filename = titleElement.textContent.trim();
artifactHash[filename] = false;
});
return artifactHash;
},
getArtifact(filename, isCurrentlySelected) {
return new Promise((resolve, reject) => {
// Find the h3 element with the matching filename
const gridContainer = document.querySelector('ul.grid');
if (!gridContainer) {
reject(new Error('Grid container not found'));
return;
}
let targetButton = null;
gridContainer.querySelectorAll('h3').forEach((titleElement) => {
if (titleElement.textContent.trim() === filename) {
// Find the clickable button that contains this h3
targetButton = titleElement.closest('button');
}
});
if (!targetButton) {
reject(new Error(`Could not find file button for: ${filename}`));
return;
}
// Click the button to open the file modal
targetButton.click();
// Wait for modal to appear and extract content
let attempts = 0;
const maxAttempts = 30; // 3 seconds total
const handleModal = () => {
// Look for the modal dialog and the content div within it
const modalDialog = document.querySelector('div[role="dialog"][data-state="open"]');
if (modalDialog) {
const contentDiv = modalDialog.querySelector('div.whitespace-pre-wrap');
if (contentDiv) {
const content = contentDiv.textContent || contentDiv.innerText || '';
// Close the modal by clicking the close button
const closeButton = modalDialog.querySelector('button.relative.can-focus svg');
if (closeButton) {
closeButton.closest('button').click();
}
resolve(content);
return;
}
}
attempts++;
if (attempts < maxAttempts) {
setTimeout(handleModal, 100);
} else {
reject(new Error(`Modal with content not found for artifact: ${filename}`));
}
};
// Start checking for modal after a brief delay
setTimeout(handleModal, 100);
});
},
async collectArtifacts() {
// TODO: Implement project page artifact collection
console.log('Project page artifact collection not yet implemented');
return { artifacts: {}, fileMap: null };
const artifactCollection = {};
let fileMap = null;
const artifactList = this.getArtifactListItems();
if (Object.keys(artifactList).length === 0) {
console.log('Nada');
return { artifacts: artifactCollection, fileMap: fileMap };
}
console.log(`Found ${Object.keys(artifactList).length} artifacts to collect`);
for (const [filename, isSelected] of Object.entries(artifactList)) {
try {
const content = await this.getArtifact(filename, isSelected);
if (filename === 'files.txt') {
fileMap = content;
console.log(`✓ Found files.txt - storing in fileMap`);
} else {
artifactCollection[filename] = content;
console.log(`✓ Collected: ${filename}`);
}
} catch (error) {
console.log(`✗ Failed to collect: ${filename} - ${error.message}`);
}
await new Promise(resolve => setTimeout(resolve, 500));
}
return { artifacts: artifactCollection, fileMap: fileMap };
}
};
const ArtifactScraper = {
async collectArtifacts() {
console.log('Collecting')
if (ProjectMeta.isProjectChat()) {
console.log('Detected project chat - using ChatArtifactScraper');
return await ChatArtifactScraper.collectArtifacts();