diff --git a/content.js b/content.js index 65a1faa..59458a7 100644 --- a/content.js +++ b/content.js @@ -21,218 +21,220 @@ return true; } - // Step 4: Open the artifact list menu - function openArtifactList() { - return new Promise((resolve, reject) => { - // Find the global artifact menu button (has the hamburger/list icon) - const artifactMenuButton = document.querySelector('button[aria-haspopup="menu"] svg[viewBox="0 0 256 256"] path[d*="M80,64a8,8,0,0,1,8-8H216"]'); - const menuButton = artifactMenuButton?.closest('button'); - - if (!menuButton) { - reject(new Error('Global artifact menu button not found')); - return; - } - - console.log('Opening artifact list menu...'); - - let hasResolved = false; - - // Listen for menu opening - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.type === 'childList') { - const addedNodes = Array.from(mutation.addedNodes); - const menuAdded = addedNodes.some(node => - node.nodeType === Node.ELEMENT_NODE && - (node.querySelector('li[role="none"] div[role="menuitem"]') || - node.getAttribute('role') === 'menu' || - node.querySelector('[role="menu"]')) - ); - - if (menuAdded) { - console.log('Artifact list menu detected as opened'); - observer.disconnect(); - if (!hasResolved) { - hasResolved = true; - resolve(true); + // Static object containing all artifact scraping functionality + const ArtifactScraper = { + + // Open the artifact list menu + openArtifactList() { + return new Promise((resolve, reject) => { + // Find the global artifact menu button (has the hamburger/list icon) + const artifactMenuButton = document.querySelector('button[aria-haspopup="menu"] svg[viewBox="0 0 256 256"] path[d*="M80,64a8,8,0,0,1,8-8H216"]'); + const menuButton = artifactMenuButton?.closest('button'); + + if (!menuButton) { + reject(new Error('Global artifact menu button not found')); + return; + } + + console.log('Opening artifact list menu...'); + + let hasResolved = false; + + // Listen for menu opening + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.type === 'childList') { + const addedNodes = Array.from(mutation.addedNodes); + const menuAdded = addedNodes.some(node => + node.nodeType === Node.ELEMENT_NODE && + (node.querySelector('li[role="none"] div[role="menuitem"]') || + node.getAttribute('role') === 'menu' || + node.querySelector('[role="menu"]')) + ); + + if (menuAdded) { + console.log('Artifact list menu detected as opened'); + observer.disconnect(); + if (!hasResolved) { + hasResolved = true; + resolve(true); + } } } + }); + }); + + observer.observe(document.body, { childList: true, subtree: true }); + + // Focus and send Enter key event to open menu + menuButton.focus(); + const enterEvent = new KeyboardEvent('keydown', { + key: 'Enter', + code: 'Enter', + bubbles: true, + cancelable: true + }); + menuButton.dispatchEvent(enterEvent); + + // Fallback timeout + setTimeout(() => { + if (!hasResolved) { + observer.disconnect(); + hasResolved = true; + reject(new Error('Failed to open artifact list menu - timeout')); + } + }, 3000); + }); + }, + + // Parse the artifact list and return filename -> isSelected hash + getArtifactListItems() { + const menuItems = document.querySelectorAll('li[role="none"] div[role="menuitem"]'); + const artifactHash = {}; + + if (menuItems.length === 0) { + console.log('No artifact list items found'); + return artifactHash; + } + + menuItems.forEach((item, index) => { + const filenameDiv = item.querySelector('div.line-clamp-2'); + if (filenameDiv) { + const filename = filenameDiv.textContent.trim(); + const isSelected = item.classList.contains('border-accent-secondary-100') || + item.getAttribute('class').includes('border-accent-secondary-100'); + + artifactHash[filename] = isSelected; + console.log(`Found artifact: ${filename} (${isSelected ? 'selected' : 'unselected'})`); + } + }); + + return artifactHash; + }, + + // Select a specific artifact list item + selectArtifactListItem(filename) { + return new Promise((resolve, reject) => { + const menuItems = document.querySelectorAll('li[role="none"] div[role="menuitem"]'); + let targetItem = null; + + // Find the matching artifact by filename + menuItems.forEach(item => { + const filenameDiv = item.querySelector('div.line-clamp-2'); + if (filenameDiv && filenameDiv.textContent.trim() === filename) { + targetItem = item; } }); - }); - - observer.observe(document.body, { childList: true, subtree: true }); - - // Focus and send Enter key event to open menu - menuButton.focus(); - const enterEvent = new KeyboardEvent('keydown', { - key: 'Enter', - code: 'Enter', - bubbles: true, - cancelable: true - }); - menuButton.dispatchEvent(enterEvent); - - // Fallback timeout - setTimeout(() => { - if (!hasResolved) { - observer.disconnect(); - hasResolved = true; - reject(new Error('Failed to open artifact list menu - timeout')); - } - }, 3000); - }); - } - - // Step 5: Parse the artifact list and return filename -> isSelected hash - function getArtifactListItems() { - const menuItems = document.querySelectorAll('li[role="none"] div[role="menuitem"]'); - const artifactHash = {}; - - if (menuItems.length === 0) { - console.log('No artifact list items found'); - return artifactHash; - } - - menuItems.forEach((item, index) => { - const filenameDiv = item.querySelector('div.line-clamp-2'); - if (filenameDiv) { - const filename = filenameDiv.textContent.trim(); - const isSelected = item.classList.contains('border-accent-secondary-100') || - item.getAttribute('class').includes('border-accent-secondary-100'); - artifactHash[filename] = isSelected; - console.log(`Found artifact: ${filename} (${isSelected ? 'selected' : 'unselected'})`); - } - }); - - return artifactHash; - } - - // Step 6: Select a specific artifact list item - function selectArtifactListItem(filename) { - return new Promise((resolve, reject) => { - const menuItems = document.querySelectorAll('li[role="none"] div[role="menuitem"]'); - let targetItem = null; - - // Find the matching artifact by filename - menuItems.forEach(item => { - const filenameDiv = item.querySelector('div.line-clamp-2'); - if (filenameDiv && filenameDiv.textContent.trim() === filename) { - targetItem = item; + if (!targetItem) { + reject(new Error(`Could not find artifact item for: ${filename}`)); + return; } + + console.log(`Selecting artifact: ${filename}`); + + // Focus and send Enter key event to select the item + targetItem.focus(); + const enterEvent = new KeyboardEvent('keydown', { + key: 'Enter', + code: 'Enter', + bubbles: true, + cancelable: true + }); + targetItem.dispatchEvent(enterEvent); + + // Wait for artifact to load + setTimeout(() => { + resolve(true); + }, 1000); }); - - if (!targetItem) { - reject(new Error(`Could not find artifact item for: ${filename}`)); - return; - } - - console.log(`Selecting artifact: ${filename}`); - - // Focus and send Enter key event to select the item - targetItem.focus(); - const enterEvent = new KeyboardEvent('keydown', { - key: 'Enter', - code: 'Enter', - bubbles: true, - cancelable: true - }); - targetItem.dispatchEvent(enterEvent); - - // Wait for artifact to load - setTimeout(() => { - resolve(true); - }, 1000); - }); - } - - // Step 7: Get artifact content and handle selection if needed - function getArtifact(filename, isCurrentlySelected) { - return new Promise(async (resolve, reject) => { - // If not currently selected, we need to select it - if (!isCurrentlySelected) { - // Open menu if not already open - const menuOpen = document.querySelector('li[role="none"] div[role="menuitem"]'); - if (!menuOpen) { + }, + + // Get artifact content and handle selection if needed + getArtifact(filename, isCurrentlySelected) { + return new Promise(async (resolve, reject) => { + // If not currently selected, we need to select it + if (!isCurrentlySelected) { + // Open menu if not already open + const menuOpen = document.querySelector('li[role="none"] div[role="menuitem"]'); + if (!menuOpen) { + try { + await this.openArtifactList(); + } catch (error) { + reject(new Error(`Failed to open menu to select: ${filename} - ${error.message}`)); + return; + } + } + + // Select the artifact try { - await openArtifactList(); + await this.selectArtifactListItem(filename); } catch (error) { - reject(new Error(`Failed to open menu to select: ${filename} - ${error.message}`)); + reject(new Error(`Failed to select artifact: ${filename} - ${error.message}`)); return; } } - // Select the artifact - try { - await selectArtifactListItem(filename); - } catch (error) { - reject(new Error(`Failed to select artifact: ${filename} - ${error.message}`)); + // Extract content from the code block using the updated selector + const codeBlock = document.querySelector('div.right-0 code'); + if (!codeBlock) { + reject(new Error(`No code block found for artifact: ${filename}`)); return; } - } + + const content = codeBlock.textContent || codeBlock.innerText || ''; + console.log(`Extracted content for ${filename}: ${content.length} characters`); + + resolve(content); + }); + }, + + // Main collection method + async collectArtifacts() { + console.log('Starting artifact collection...'); - // Extract content from the code block - const codeBlock = document.querySelector('div.right-0 code'); - if (!codeBlock) { - reject(new Error(`No code block found for artifact: ${filename}`)); + // Initialize empty collection + const artifactCollection = {}; + + // Open artifact list + await this.openArtifactList(); + + // Get list of artifacts + const artifactList = this.getArtifactListItems(); + + if (Object.keys(artifactList).length === 0) { + console.log('No artifacts found in list'); return; } - const content = codeBlock.textContent || codeBlock.innerText || ''; - console.log(`Extracted content for ${filename}: ${content.length} characters`); + console.log(`Found ${Object.keys(artifactList).length} artifacts to collect`); - resolve(content); - }); - } - - // Step 2: Main collection method - async function collectArtifacts() { - console.log('Starting artifact collection...'); - - // Step 3: Initialize empty collection - const artifactCollection = {}; - - // Step 4: Open artifact list - const menuOpened = await openArtifactList(); - if (!menuOpened) { - console.log('Failed to open artifact list'); - return; - } - - // Step 5: Get list of artifacts - const artifactList = getArtifactListItems(); - - if (Object.keys(artifactList).length === 0) { - console.log('No artifacts found in list'); - return; - } - - console.log(`Found ${Object.keys(artifactList).length} artifacts to collect`); - - // Step 6-8: Iterate through artifacts and collect content - for (const [filename, isSelected] of Object.entries(artifactList)) { - console.log(`Processing artifact: ${filename}`); - - try { - const content = await getArtifact(filename, isSelected); - artifactCollection[filename] = content; - console.log(`✓ Collected: ${filename}`); - } catch (error) { - console.log(`✗ Failed to collect: ${filename} - ${error.message}`); + // Iterate through artifacts and collect content + for (const [filename, isSelected] of Object.entries(artifactList)) { + console.log(`Processing artifact: ${filename}`); + + try { + const content = await this.getArtifact(filename, isSelected); + artifactCollection[filename] = content; + console.log(`✓ Collected: ${filename}`); + } catch (error) { + console.log(`✗ Failed to collect: ${filename} - ${error.message}`); + } + + // Small delay between artifacts + await new Promise(resolve => setTimeout(resolve, 500)); } - // Small delay between artifacts - await new Promise(resolve => setTimeout(resolve, 500)); + // Log final collection + console.log('=== ARTIFACT COLLECTION COMPLETE ==='); + console.log(`Collected ${Object.keys(artifactCollection).length} artifacts:`); + console.log(artifactCollection); + + return artifactCollection; } - - // Step 9: Log final collection - console.log('=== ARTIFACT COLLECTION COMPLETE ==='); - console.log(`Collected ${Object.keys(artifactCollection).length} artifacts:`); - console.log(artifactCollection); - } + }; - // Step 1: Content script is loaded, start collection + // Main function async function main() { console.log('Running artifact collector'); @@ -240,7 +242,7 @@ return; } - await collectArtifacts(); + await ArtifactScraper.collectArtifacts(); } // Run the main function