153 lines
4.5 KiB
JavaScript
153 lines
4.5 KiB
JavaScript
const ArtifactExporter = {
|
|
|
|
parseFileMap(fileMap) {
|
|
const lines = fileMap.split('\n').filter(line => line.trim());
|
|
const pathMapping = {};
|
|
let currentPath = '';
|
|
|
|
lines.forEach(line => {
|
|
const trimmedLine = line.trim();
|
|
|
|
if (trimmedLine.endsWith('/')) {
|
|
currentPath = trimmedLine;
|
|
} else if (trimmedLine && !trimmedLine.startsWith('/')) {
|
|
if (line.startsWith(' ')) {
|
|
pathMapping[trimmedLine] = currentPath + trimmedLine;
|
|
} else {
|
|
pathMapping[trimmedLine] = trimmedLine;
|
|
}
|
|
}
|
|
});
|
|
|
|
return pathMapping;
|
|
},
|
|
|
|
extractFilenamesFromFileMap(fileMap) {
|
|
const lines = fileMap.split('\n').filter(line => line.trim());
|
|
const filenames = [];
|
|
|
|
lines.forEach(line => {
|
|
const trimmedLine = line.trim();
|
|
|
|
if (trimmedLine.endsWith('/') || !trimmedLine || trimmedLine.startsWith('/')) {
|
|
return;
|
|
}
|
|
|
|
const filename = trimmedLine.replace(/^\s+/, '');
|
|
if (filename) {
|
|
filenames.push(filename);
|
|
}
|
|
});
|
|
|
|
return filenames.sort((a, b) => b.length - a.length);
|
|
},
|
|
|
|
extractFilenameFromArtifactName(artifactName, sortedFilenames) {
|
|
for (const filename of sortedFilenames) {
|
|
const lowerArtifactName = artifactName.toLowerCase();
|
|
const lowerFilename = filename.toLowerCase();
|
|
|
|
if (lowerArtifactName.includes(lowerFilename)) {
|
|
return filename;
|
|
}
|
|
}
|
|
|
|
return artifactName;
|
|
},
|
|
|
|
createFinalMapping(artifactCollection, pathMapping) {
|
|
const finalMap = {};
|
|
|
|
const sortedFilenames = this.extractFilenamesFromFileMap(Object.keys(pathMapping).join('\n'));
|
|
|
|
Object.entries(artifactCollection).forEach(([artifactName, content]) => {
|
|
const extractedFilename = this.extractFilenameFromArtifactName(artifactName, sortedFilenames);
|
|
const fullPath = pathMapping[extractedFilename] || extractedFilename;
|
|
|
|
if (artifactName !== extractedFilename) {
|
|
console.log(`Mapped artifact "${artifactName}" -> found filename "${extractedFilename}" -> "${fullPath}"`);
|
|
} else {
|
|
console.log(`Mapped artifact "${artifactName}" -> "${fullPath}"`);
|
|
}
|
|
|
|
finalMap[fullPath] = content;
|
|
});
|
|
|
|
return finalMap;
|
|
},
|
|
|
|
generateZipFilename() {
|
|
const now = new Date();
|
|
const timestamp = now.getFullYear() + '-' +
|
|
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
|
String(now.getDate()).padStart(2, '0');
|
|
return `${timestamp}_claude-artifacts.zip`;
|
|
},
|
|
|
|
addFilesToZip(zip, finalMap) {
|
|
Object.entries(finalMap).forEach(([fullPath, content]) => {
|
|
const pathParts = fullPath.split('/');
|
|
|
|
if (pathParts.length > 1) {
|
|
const filename = pathParts.pop();
|
|
const folderPath = pathParts.join('/');
|
|
const folder = zip.folder(folderPath);
|
|
folder.file(filename, content);
|
|
} else {
|
|
zip.file(fullPath, content);
|
|
}
|
|
});
|
|
},
|
|
|
|
downloadZip(content, filename) {
|
|
const url = URL.createObjectURL(content);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
|
|
console.log(`✓ Downloaded: ${filename}`);
|
|
},
|
|
|
|
exportArtifacts(artifactCollection, fileMap) {
|
|
// Validation
|
|
if (!artifactCollection || Object.keys(artifactCollection).length === 0) {
|
|
console.warn("No non-filemap artifacts were found. Ask the LLM to copy them from the project to this chat first.");
|
|
return;
|
|
}
|
|
if (!fileMap) {
|
|
console.warn("No filemap was found. Ask the LLM to generate one. See the README for a sample prompt.");
|
|
return;
|
|
}
|
|
|
|
// Parse the file map into paths
|
|
const pathMapping = this.parseFileMap(fileMap);
|
|
|
|
// Create final map with full paths and contents
|
|
const finalMap = this.createFinalMapping(artifactCollection, pathMapping);
|
|
|
|
// Create zip file
|
|
const zip = new JSZip();
|
|
|
|
// Add each file to the zip
|
|
this.addFilesToZip(zip, finalMap);
|
|
|
|
// Generate filename
|
|
const zipFilename = this.generateZipFilename();
|
|
|
|
// Generate and download the zip
|
|
zip.generateAsync({type: "blob"})
|
|
.then((content) => {
|
|
this.downloadZip(content, zipFilename);
|
|
})
|
|
.catch((error) => {
|
|
console.error('Failed to generate zip:', error);
|
|
});
|
|
}
|
|
};
|
|
|
|
// No exports needed - will be in same scope after build
|