Sucessfully fetch filenames from triggered menu
This commit is contained in:
243
content.js
243
content.js
@ -21,48 +21,163 @@
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to trigger context menu and extract filename
|
// Function to get all artifact filenames from the global artifact menu
|
||||||
function getArtifactFilename(artifact) {
|
function getAllArtifactFilenames() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// Look for context menu trigger button (usually three dots or similar)
|
// Find the global artifact menu button (has the hamburger/list icon)
|
||||||
const contextMenuTrigger = artifact.querySelector('[data-testid*="menu"], [aria-label*="menu"], button[aria-haspopup="menu"]');
|
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 (!contextMenuTrigger) {
|
if (!menuButton) {
|
||||||
console.log('No context menu trigger found for artifact');
|
console.log('Global artifact menu button not found');
|
||||||
resolve(null);
|
resolve([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to handle menu opening
|
console.log('Found global artifact menu button, attempting to trigger...');
|
||||||
function handleMenuOpen() {
|
|
||||||
// Wait a bit for menu to render
|
let hasResolved = false;
|
||||||
|
|
||||||
|
// Function to extract filenames from the opened menu
|
||||||
|
function extractFilenames() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Look for download or filename-related menu items
|
if (hasResolved) return;
|
||||||
const menuItems = document.querySelectorAll('[role="menuitem"], [data-testid*="download"], [data-testid*="save"]');
|
|
||||||
|
|
||||||
let filename = null;
|
const menuItems = document.querySelectorAll('li[role="none"] div[role="menuitem"]');
|
||||||
|
const filenames = [];
|
||||||
|
|
||||||
menuItems.forEach(item => {
|
if (menuItems.length === 0) {
|
||||||
const text = item.textContent || item.getAttribute('aria-label') || '';
|
console.log('No menu items found - menu may not have opened');
|
||||||
|
if (!hasResolved) {
|
||||||
|
hasResolved = true;
|
||||||
|
resolve([]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Look for download options that might contain filename
|
menuItems.forEach((item, index) => {
|
||||||
if (text.toLowerCase().includes('download') || text.toLowerCase().includes('save')) {
|
// Get the filename from the first div (line-clamp-2 class)
|
||||||
console.log('Found menu item:', text);
|
const filenameDiv = item.querySelector('div.line-clamp-2');
|
||||||
|
if (filenameDiv) {
|
||||||
|
const filename = filenameDiv.textContent.trim();
|
||||||
|
|
||||||
// Try to extract filename from the text
|
// Check if this is the currently selected item
|
||||||
// Common patterns: "Download filename.ext", "Save as filename.ext", etc.
|
const isSelected = item.classList.contains('border-accent-secondary-100') ||
|
||||||
const filenameMatch = text.match(/(?:download|save(?:\s+as)?)\s+(.+\.\w+)/i);
|
item.getAttribute('class').includes('border-accent-secondary-100');
|
||||||
if (filenameMatch) {
|
|
||||||
filename = filenameMatch[1].trim();
|
filenames.push({
|
||||||
}
|
filename: filename,
|
||||||
|
isSelected: isSelected,
|
||||||
|
element: item,
|
||||||
|
index: index
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Found artifact: ${filename} (${isSelected ? 'selected' : 'unselected'})`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close the menu by clicking elsewhere or pressing escape
|
// Close the menu by clicking elsewhere
|
||||||
document.body.click();
|
document.body.click();
|
||||||
|
|
||||||
resolve(filename);
|
if (!hasResolved) {
|
||||||
}, 100);
|
hasResolved = true;
|
||||||
|
resolve(filenames);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for menu opening with more comprehensive detection
|
||||||
|
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('Menu detected as opened');
|
||||||
|
observer.disconnect();
|
||||||
|
extractFilenames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
// Focus and send Enter key (this worked!)
|
||||||
|
menuButton.focus();
|
||||||
|
const enterEvent = new KeyboardEvent('keydown', {
|
||||||
|
key: 'Enter',
|
||||||
|
code: 'Enter',
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true
|
||||||
|
});
|
||||||
|
menuButton.dispatchEvent(enterEvent);
|
||||||
|
|
||||||
|
// Final fallback timeout - only if we haven't resolved yet
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!hasResolved) {
|
||||||
|
observer.disconnect();
|
||||||
|
console.log('Menu trigger timeout - no menu detected');
|
||||||
|
hasResolved = true;
|
||||||
|
resolve([]);
|
||||||
|
}
|
||||||
|
}, 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
|
// Listen for menu opening
|
||||||
@ -72,15 +187,13 @@
|
|||||||
const addedNodes = Array.from(mutation.addedNodes);
|
const addedNodes = Array.from(mutation.addedNodes);
|
||||||
const menuAdded = addedNodes.some(node =>
|
const menuAdded = addedNodes.some(node =>
|
||||||
node.nodeType === Node.ELEMENT_NODE &&
|
node.nodeType === Node.ELEMENT_NODE &&
|
||||||
(node.getAttribute('role') === 'menu' ||
|
(node.querySelector('li[role="none"] div[role="menuitem"]') ||
|
||||||
node.querySelector('[role="menu"]') ||
|
node.getAttribute('role') === 'menu')
|
||||||
node.classList?.contains('menu') ||
|
|
||||||
node.getAttribute('data-testid')?.includes('menu'))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (menuAdded) {
|
if (menuAdded) {
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
handleMenuOpen();
|
clickArtifact();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -88,59 +201,69 @@
|
|||||||
|
|
||||||
observer.observe(document.body, { childList: true, subtree: true });
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
|
|
||||||
// Trigger the context menu
|
// Click the menu button
|
||||||
contextMenuTrigger.click();
|
menuButton.click();
|
||||||
|
|
||||||
// Fallback timeout
|
// Fallback timeout
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
if (filename === null) {
|
resolve(false);
|
||||||
resolve(null);
|
}, 3000);
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find and process artifacts
|
// Find and process artifacts using the global menu approach
|
||||||
async function processArtifacts() {
|
async function processArtifacts() {
|
||||||
if (!isProjectChat()) {
|
if (!isProjectChat()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for artifacts in the page
|
console.log('Getting artifact list from global menu...');
|
||||||
// Try multiple selectors that might match artifacts
|
|
||||||
const artifacts = document.querySelectorAll('[data-testid*="artifact"], .artifact, [class*="artifact"], iframe[title*="artifact"]');
|
|
||||||
|
|
||||||
if (artifacts.length === 0) {
|
// Get all available artifacts from the global menu
|
||||||
console.log('No artifacts found on this page');
|
const artifactInfos = await getAllArtifactFilenames();
|
||||||
|
|
||||||
|
if (artifactInfos.length === 0) {
|
||||||
|
console.log('No artifacts found in global menu');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Found ${artifacts.length} artifacts`);
|
console.log(`Found ${artifactInfos.length} artifacts in global menu`);
|
||||||
|
|
||||||
// Process each artifact one by one to avoid conflicts
|
// Process each artifact
|
||||||
for (let i = 0; i < artifacts.length; i++) {
|
for (let i = 0; i < artifactInfos.length; i++) {
|
||||||
const artifact = artifacts[i];
|
const artifactInfo = artifactInfos[i];
|
||||||
console.log(`Processing artifact ${i + 1}`);
|
console.log(`Processing artifact: ${artifactInfo.filename}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try to get the filename from context menu
|
// Select this artifact if it's not already selected
|
||||||
const filename = await getArtifactFilename(artifact);
|
const selected = await selectArtifact(artifactInfo);
|
||||||
|
|
||||||
if (filename) {
|
if (!selected) {
|
||||||
console.log(`Artifact ${i + 1} filename: ${filename}`);
|
console.log(`Failed to select artifact: ${artifactInfo.filename}`);
|
||||||
} else {
|
continue;
|
||||||
console.log(`Artifact ${i + 1} filename: Could not determine filename`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract artifact content - this will depend on the actual structure
|
// Wait a moment for the artifact to fully load
|
||||||
const content = artifact.textContent || artifact.innerHTML;
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
console.log(`Artifact ${i + 1} content preview: ${content.substring(0, 100)}...`);
|
|
||||||
|
|
||||||
// TODO: Add download functionality here
|
// 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) {
|
} catch (error) {
|
||||||
console.error(`Error processing artifact ${i + 1}:`, error);
|
console.error(`Error processing artifact ${artifactInfo.filename}:`, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Small delay between processing artifacts
|
// Small delay between processing artifacts
|
||||||
|
|||||||
Reference in New Issue
Block a user