vzcode
Version:
Multiplayer code editor system
195 lines (174 loc) • 4.94 kB
text/typescript
import {
formatMarkdownFiles,
parseMarkdownFiles,
} from 'llm-code-format';
import {
mergeFileChanges,
prepareFilesForPrompt,
} from 'editcodewithai';
import { VizFiles } from '@vizhub/viz-types';
import { generateRunId } from '@vizhub/viz-utils';
import JSZip from 'jszip';
export const createAICopyPasteHandlers = (
files: VizFiles,
submitOperation: any,
setCopyButtonText: (text: string) => void,
setPasteButtonText: (text: string) => void,
setExportButtonText: (text: string) => void,
) => {
// Copy for AI - formats all files and copies to clipboard
const handleCopyForAI = async () => {
if (!files || Object.keys(files).length === 0) {
setCopyButtonText('No files to copy');
setTimeout(
() => setCopyButtonText('Copy for AI'),
2000,
);
return;
}
try {
// Apply truncation logic to reduce token usage
const { files: truncatedFiles, imageFiles } =
prepareFilesForPrompt(files);
// Format truncated files for markdown
const formattedFiles =
formatMarkdownFiles(truncatedFiles);
// Add metadata about skipped image files
let finalContent = formattedFiles;
if (imageFiles.length > 0) {
finalContent +=
'\n\n<!-- Image files (not included): ' +
imageFiles.join(', ') +
' -->';
}
// Copy to clipboard
await navigator.clipboard.writeText(finalContent);
// Show success feedback
setCopyButtonText('Copied!');
setTimeout(
() => setCopyButtonText('Copy for AI'),
2000,
);
} catch (error) {
console.error('Failed to copy files for AI:', error);
setCopyButtonText('Error');
setTimeout(
() => setCopyButtonText('Copy for AI'),
2000,
);
}
};
// Paste for AI - parses clipboard content and updates files
const handlePasteForAI = async () => {
if (!submitOperation) return;
try {
// Read from clipboard
const clipboardText =
await navigator.clipboard.readText();
// Handle various line endings
const normalized = clipboardText.replace(
/\r\n?/g,
'\n',
);
if (!normalized.trim()) {
setPasteButtonText('Empty clipboard');
setTimeout(
() => setPasteButtonText('Paste for AI'),
2000,
);
return;
}
// Parse the markdown files format
const parsed = parseMarkdownFiles(normalized, 'bold');
if (
parsed.files &&
Object.keys(parsed.files).length > 0
) {
// Use mergeFileChanges utility from editcodewithai
const mergedFiles = mergeFileChanges(
files || {},
parsed.files,
);
// Update files using the submit operation
submitOperation((document) => ({
...document,
files: mergedFiles,
runId: generateRunId(),
}));
setPasteButtonText('Pasted!');
setTimeout(
() => setPasteButtonText('Paste for AI'),
2000,
);
} else {
setPasteButtonText('No files found');
setTimeout(
() => setPasteButtonText('Paste for AI'),
2000,
);
}
} catch (error) {
console.error(
'Failed to paste files from AI:',
error,
);
setPasteButtonText('Error');
setTimeout(
() => setPasteButtonText('Paste for AI'),
2000,
);
}
};
// Export to ZIP - creates and downloads a ZIP file with all viz files
const handleExportToZip = async () => {
if (!files || Object.keys(files).length === 0) {
setExportButtonText('No files to export');
setTimeout(
() => setExportButtonText('Export to ZIP'),
2000,
);
return;
}
try {
// Create the ZIP
const zip = new JSZip();
// Add each file to the ZIP
Object.entries(files).forEach(([fileId, file]) => {
const fileName = file.name || fileId;
const fileContent = file.text || '';
zip.file(fileName, fileContent);
});
// Generate and download
const blob = await zip.generateAsync({
type: 'blob',
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'viz-files.zip';
a.click();
URL.revokeObjectURL(url);
// Show success feedback
setExportButtonText('Exported!');
setTimeout(
() => setExportButtonText('Export to ZIP'),
2000,
);
} catch (error) {
console.error(
'Failed to export files to ZIP:',
error,
);
setExportButtonText('Error');
setTimeout(
() => setExportButtonText('Export to ZIP'),
2000,
);
}
};
return {
handleCopyForAI,
handlePasteForAI,
handleExportToZip,
};
};