Functional scraping of the project page sans files.txt
This commit is contained in:
@ -2,7 +2,7 @@ browser.browserAction.onClicked.addListener((tab) => {
|
|||||||
console.log('Extension clicked - triggering artifact collection.');
|
console.log('Extension clicked - triggering artifact collection.');
|
||||||
|
|
||||||
// Check if we're on the correct domain and path
|
// 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');
|
console.log('Not on claude.ai chat page');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
114
src/scraper.js
114
src/scraper.js
@ -234,18 +234,124 @@ const ChatArtifactScraper = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ProjectArtifactScraper = {
|
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() {
|
async collectArtifacts() {
|
||||||
// TODO: Implement project page artifact collection
|
const artifactCollection = {};
|
||||||
console.log('Project page artifact collection not yet implemented');
|
let fileMap = null;
|
||||||
return { artifacts: {}, 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 = {
|
const ArtifactScraper = {
|
||||||
|
|
||||||
async collectArtifacts() {
|
async collectArtifacts() {
|
||||||
|
console.log('Collecting')
|
||||||
if (ProjectMeta.isProjectChat()) {
|
if (ProjectMeta.isProjectChat()) {
|
||||||
console.log('Detected project chat - using ChatArtifactScraper');
|
console.log('Detected project chat - using ChatArtifactScraper');
|
||||||
return await ChatArtifactScraper.collectArtifacts();
|
return await ChatArtifactScraper.collectArtifacts();
|
||||||
|
|||||||
Reference in New Issue
Block a user