generated from coulomb/repo-seed
feat: add folder picker for automatic project file loading
- Add "Load Project Folder" button with folder selection (webkitdirectory) - Automatically load all project files (JSON, CSV, SVG, CSS) from selected folder - Eliminate need to manually upload each file individually - Show clear errors if referenced files are missing from folder - Update all documentation to explain folder picker usage This solves the browser security limitation where uploading a single project.json doesn't allow automatic access to other files in the same directory. Users can now select an entire project folder and all files load automatically in one click. Changes: - index.html: Add folder input with webkitdirectory attribute and UI - engine.js: Add folderInput event handler to process all files from folder - README.md: Document folder picker as primary loading method - WINDOWS_USAGE.md: Add folder picker as recommended Option 1 - TROUBLESHOOTING.md: Add section explaining project files not auto-loading - CLAUDE.md: Document folder picker architecture for future instances - Makefile: Update DIST_README.md template to mention folder picker Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
103
engine.js
103
engine.js
@@ -668,6 +668,109 @@ window.svgViewer = {
|
||||
// --------- UI event handlers ---------
|
||||
|
||||
window.setupEventHandlers = function() {
|
||||
// Handler for loading entire project folder
|
||||
const folderInput = document.getElementById("folderInput");
|
||||
if (folderInput) {
|
||||
folderInput.addEventListener("change", async (ev) => {
|
||||
const files = Array.from(ev.target.files);
|
||||
if (!files.length) return;
|
||||
|
||||
console.log("Folder selected with", files.length, "files");
|
||||
|
||||
try {
|
||||
// Find project.json in the uploaded files
|
||||
const projectFile = files.find(f => f.name === 'project.json');
|
||||
if (!projectFile) {
|
||||
alert('No project.json found in selected folder. Please select a folder containing a project.json file.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse project.json
|
||||
const projectText = await projectFile.text();
|
||||
const cfg = JSON.parse(projectText);
|
||||
console.log("Loaded project configuration:", cfg);
|
||||
|
||||
// Clear projectBasePath since we're loading from uploaded files
|
||||
window.timelineEngine.projectBasePath = '';
|
||||
|
||||
// Update project status
|
||||
window.timelineEngine.updateFileStatus('project', projectFile.name, 'loaded');
|
||||
|
||||
// Load referenced files from the folder
|
||||
const errors = [];
|
||||
|
||||
// Load stylesheet
|
||||
if (cfg.stylesheet) {
|
||||
const cssFile = files.find(f => f.name === cfg.stylesheet || f.webkitRelativePath.endsWith(cfg.stylesheet));
|
||||
if (cssFile) {
|
||||
const cssText = await cssFile.text();
|
||||
window.timelineEngine.cssOverride = true;
|
||||
const blob = new Blob([cssText], { type: "text/css" });
|
||||
document.getElementById("dynamicCss").href = URL.createObjectURL(blob);
|
||||
window.timelineEngine.updateFileStatus('css', cssFile.name, 'loaded');
|
||||
console.log("Loaded stylesheet:", cssFile.name);
|
||||
} else {
|
||||
errors.push(`Stylesheet: ${cfg.stylesheet}`);
|
||||
window.timelineEngine.updateFileStatus('css', cfg.stylesheet, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Load SVG template
|
||||
if (cfg.svgTemplate) {
|
||||
const svgFile = files.find(f => f.name === cfg.svgTemplate || f.webkitRelativePath.endsWith(cfg.svgTemplate));
|
||||
if (svgFile) {
|
||||
window.timelineEngine.template = await svgFile.text();
|
||||
window.timelineEngine.updateFileStatus('svg', svgFile.name, 'loaded');
|
||||
window.timelineEngine.showTemplateFields();
|
||||
console.log("Loaded SVG template:", svgFile.name);
|
||||
} else {
|
||||
errors.push(`SVG template: ${cfg.svgTemplate}`);
|
||||
window.timelineEngine.updateFileStatus('svg', cfg.svgTemplate, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Load CSV data
|
||||
if (cfg.dataSource) {
|
||||
const csvFile = files.find(f => f.name === cfg.dataSource || f.webkitRelativePath.endsWith(cfg.dataSource));
|
||||
if (csvFile) {
|
||||
const csvText = await csvFile.text();
|
||||
window.timelineEngine.csvOverride = true;
|
||||
window.timelineEngine.updateFileStatus('csv', csvFile.name, 'loaded');
|
||||
console.log("Loaded CSV data:", csvFile.name);
|
||||
|
||||
// Set config and process CSV
|
||||
window.timelineEngine.config = cfg;
|
||||
window.timelineEngine.processCsv(csvText);
|
||||
} else {
|
||||
errors.push(`CSV data: ${cfg.dataSource}`);
|
||||
window.timelineEngine.updateFileStatus('csv', cfg.dataSource, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Update project name and description
|
||||
const name = cfg.name || "Timeline";
|
||||
document.getElementById("projectName").textContent = name;
|
||||
document.getElementById("projectSubtitle").textContent = cfg.description || "Project loaded from folder.";
|
||||
|
||||
// Show field mappings
|
||||
window.timelineEngine.showFieldMappings();
|
||||
|
||||
// Show any errors
|
||||
if (errors.length > 0) {
|
||||
setTimeout(() => {
|
||||
alert(`⚠️ Some files could not be loaded:\n\n${errors.join('\n')}\n\nMake sure all referenced files are in the selected folder.`);
|
||||
}, 500);
|
||||
} else {
|
||||
console.log("✅ All project files loaded successfully from folder");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error loading project folder:", error);
|
||||
alert(`Error loading project: ${error.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const projectInput = document.getElementById("projectInput");
|
||||
if (projectInput) {
|
||||
projectInput.addEventListener("change", async (ev) => {
|
||||
|
||||
Reference in New Issue
Block a user