Correctly collects artifact data
This commit is contained in:
368
content.js
368
content.js
@ -21,72 +21,23 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to get all artifact filenames from the global artifact menu
|
||||
function getAllArtifactFilenames() {
|
||||
return new Promise((resolve) => {
|
||||
// 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) {
|
||||
console.log('Global artifact menu button not found');
|
||||
resolve([]);
|
||||
reject(new Error('Global artifact menu button not found'));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Found global artifact menu button, attempting to trigger...');
|
||||
console.log('Opening artifact list menu...');
|
||||
|
||||
let hasResolved = false;
|
||||
|
||||
// Function to extract filenames from the opened menu
|
||||
function extractFilenames() {
|
||||
setTimeout(() => {
|
||||
if (hasResolved) return;
|
||||
|
||||
const menuItems = document.querySelectorAll('li[role="none"] div[role="menuitem"]');
|
||||
const filenames = [];
|
||||
|
||||
if (menuItems.length === 0) {
|
||||
console.log('No menu items found - menu may not have opened');
|
||||
if (!hasResolved) {
|
||||
hasResolved = true;
|
||||
resolve([]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
menuItems.forEach((item, index) => {
|
||||
// Get the filename from the first div (line-clamp-2 class)
|
||||
const filenameDiv = item.querySelector('div.line-clamp-2');
|
||||
if (filenameDiv) {
|
||||
const filename = filenameDiv.textContent.trim();
|
||||
|
||||
// Check if this is the currently selected item
|
||||
const isSelected = item.classList.contains('border-accent-secondary-100') ||
|
||||
item.getAttribute('class').includes('border-accent-secondary-100');
|
||||
|
||||
filenames.push({
|
||||
filename: filename,
|
||||
isSelected: isSelected,
|
||||
element: item,
|
||||
index: index
|
||||
});
|
||||
|
||||
console.log(`Found artifact: ${filename} (${isSelected ? 'selected' : 'unselected'})`);
|
||||
}
|
||||
});
|
||||
|
||||
// Close the menu by clicking elsewhere
|
||||
document.body.click();
|
||||
|
||||
if (!hasResolved) {
|
||||
hasResolved = true;
|
||||
resolve(filenames);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// Listen for menu opening with more comprehensive detection
|
||||
// Listen for menu opening
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'childList') {
|
||||
@ -99,9 +50,12 @@
|
||||
);
|
||||
|
||||
if (menuAdded) {
|
||||
console.log('Menu detected as opened');
|
||||
console.log('Artifact list menu detected as opened');
|
||||
observer.disconnect();
|
||||
extractFilenames();
|
||||
if (!hasResolved) {
|
||||
hasResolved = true;
|
||||
resolve(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -109,7 +63,7 @@
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
// Focus and send Enter key (this worked!)
|
||||
// Focus and send Enter key event to open menu
|
||||
menuButton.focus();
|
||||
const enterEvent = new KeyboardEvent('keydown', {
|
||||
key: 'Enter',
|
||||
@ -119,162 +73,174 @@
|
||||
});
|
||||
menuButton.dispatchEvent(enterEvent);
|
||||
|
||||
// Final fallback timeout - only if we haven't resolved yet
|
||||
// Fallback timeout
|
||||
setTimeout(() => {
|
||||
if (!hasResolved) {
|
||||
observer.disconnect();
|
||||
console.log('Menu trigger timeout - no menu detected');
|
||||
hasResolved = true;
|
||||
resolve([]);
|
||||
reject(new Error('Failed to open artifact list menu - timeout'));
|
||||
}
|
||||
}, 4000);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to select a specific artifact by clicking on it in the menu
|
||||
function selectArtifact(artifactInfo) {
|
||||
return new Promise((resolve) => {
|
||||
if (artifactInfo.isSelected) {
|
||||
console.log(`${artifactInfo.filename} is already selected`);
|
||||
resolve(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find and click the global menu button again
|
||||
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) {
|
||||
console.log('Could not find menu button to select artifact');
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Function to click the specific artifact
|
||||
function clickArtifact() {
|
||||
setTimeout(() => {
|
||||
const menuItems = document.querySelectorAll('li[role="none"] div[role="menuitem"]');
|
||||
|
||||
// Find the matching artifact by filename
|
||||
let targetItem = null;
|
||||
menuItems.forEach(item => {
|
||||
const filenameDiv = item.querySelector('div.line-clamp-2');
|
||||
if (filenameDiv && filenameDiv.textContent.trim() === artifactInfo.filename) {
|
||||
targetItem = item;
|
||||
}
|
||||
});
|
||||
|
||||
if (targetItem) {
|
||||
console.log(`Clicking on ${artifactInfo.filename}`);
|
||||
targetItem.click();
|
||||
|
||||
// Wait a moment for the artifact to load
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, 500);
|
||||
} else {
|
||||
console.log(`Could not find ${artifactInfo.filename} in menu`);
|
||||
document.body.click(); // Close menu
|
||||
resolve(false);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// 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')
|
||||
);
|
||||
|
||||
if (menuAdded) {
|
||||
observer.disconnect();
|
||||
clickArtifact();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
|
||||
// Click the menu button
|
||||
menuButton.click();
|
||||
|
||||
// Fallback timeout
|
||||
setTimeout(() => {
|
||||
observer.disconnect();
|
||||
resolve(false);
|
||||
}, 3000);
|
||||
});
|
||||
}
|
||||
|
||||
// Find and process artifacts using the global menu approach
|
||||
async function processArtifacts() {
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
// 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) {
|
||||
try {
|
||||
await openArtifactList();
|
||||
} catch (error) {
|
||||
reject(new Error(`Failed to open menu to select: ${filename} - ${error.message}`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Select the artifact
|
||||
try {
|
||||
await selectArtifactListItem(filename);
|
||||
} catch (error) {
|
||||
reject(new Error(`Failed to select artifact: ${filename} - ${error.message}`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const content = codeBlock.textContent || codeBlock.innerText || '';
|
||||
console.log(`Extracted content for ${filename}: ${content.length} characters`);
|
||||
|
||||
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}`);
|
||||
}
|
||||
|
||||
// Small delay between artifacts
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
|
||||
// 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
|
||||
async function main() {
|
||||
console.log('Running artifact collector');
|
||||
|
||||
if (!isProjectChat()) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Getting artifact list from global menu...');
|
||||
|
||||
// Get all available artifacts from the global menu
|
||||
const artifactInfos = await getAllArtifactFilenames();
|
||||
|
||||
if (artifactInfos.length === 0) {
|
||||
console.log('No artifacts found in global menu');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Found ${artifactInfos.length} artifacts in global menu`);
|
||||
|
||||
// Process each artifact
|
||||
for (let i = 0; i < artifactInfos.length; i++) {
|
||||
const artifactInfo = artifactInfos[i];
|
||||
console.log(`Processing artifact: ${artifactInfo.filename}`);
|
||||
|
||||
try {
|
||||
// Select this artifact if it's not already selected
|
||||
const selected = await selectArtifact(artifactInfo);
|
||||
|
||||
if (!selected) {
|
||||
console.log(`Failed to select artifact: ${artifactInfo.filename}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wait a moment for the artifact to fully load
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Now extract the content from the currently displayed artifact
|
||||
// This will depend on how artifacts are rendered in the UI
|
||||
const artifactContainer = document.querySelector('[data-testid*="artifact"], .artifact, iframe[title*="artifact"]');
|
||||
|
||||
if (artifactContainer) {
|
||||
const content = artifactContainer.textContent || artifactContainer.innerHTML;
|
||||
console.log(`${artifactInfo.filename} content preview: ${content.substring(0, 100)}...`);
|
||||
|
||||
// TODO: Add download functionality here
|
||||
// downloadFile(artifactInfo.filename, content);
|
||||
|
||||
} else {
|
||||
console.log(`Could not find artifact content container for ${artifactInfo.filename}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error processing artifact ${artifactInfo.filename}:`, error);
|
||||
}
|
||||
|
||||
// Small delay between processing artifacts
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
}
|
||||
}
|
||||
|
||||
// Main function to run when content script is executed
|
||||
async function main() {
|
||||
console.log('Running artifact processor');
|
||||
await processArtifacts();
|
||||
await collectArtifacts();
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
|
||||
Reference in New Issue
Block a user