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:
2026-01-23 16:34:35 +01:00
parent 39037587ba
commit cefbf96a82
7 changed files with 180 additions and 6 deletions

View File

@@ -149,6 +149,13 @@ Use this to deploy the application to Windows or other environments. The distrib
- Internal view: Shows item IDs (for development/review)
- External view: Hides item IDs (for presentations/exports)
**Project Loading Methods**:
- Auto-load: When served via HTTP, attempts to load from binect/, my-project/, or example/ folders
- Folder picker: User selects entire project folder - all files load automatically (uses webkitdirectory attribute)
- Individual files: User manually uploads project.json, then CSV/SVG/CSS separately
The folder picker was added to solve the browser security limitation where uploading a single project.json doesn't allow automatic access to other files in the same directory.
## Project Structure
```

View File

@@ -204,12 +204,14 @@ dist:
@echo "1. Double-click \`start-server.bat\`" >> dist/DIST_README.md
@echo "2. Your browser will open automatically at http://localhost:8000" >> dist/DIST_README.md
@echo "3. The application will load the example project automatically" >> dist/DIST_README.md
@echo "4. To load your own project, click **📂 Load Project Folder** and select your project directory" >> dist/DIST_README.md
@echo "" >> dist/DIST_README.md
@echo "### Linux / Mac" >> dist/DIST_README.md
@echo "" >> dist/DIST_README.md
@echo "1. Run \`./start-server.sh\` (or \`bash start-server.sh\`)" >> dist/DIST_README.md
@echo "2. Your browser will open automatically at http://localhost:8000" >> dist/DIST_README.md
@echo "3. The application will load the example project automatically" >> dist/DIST_README.md
@echo "4. To load your own project, click **📂 Load Project Folder** and select your project directory" >> dist/DIST_README.md
@echo "" >> dist/DIST_README.md
@echo "### Alternative: Open Directly" >> dist/DIST_README.md
@echo "" >> dist/DIST_README.md
@@ -233,7 +235,9 @@ dist:
@echo "3. Edit \`project.json\` to configure your timeline" >> dist/DIST_README.md
@echo "4. Replace \`sample.csv\` with your data" >> dist/DIST_README.md
@echo "5. Customize \`style.css\` and \`template-v2.svg\` as needed" >> dist/DIST_README.md
@echo "6. Load your project in the application using the file upload controls" >> dist/DIST_README.md
@echo "6. Click **📂 Load Project Folder** and select your project folder - all files load automatically!" >> dist/DIST_README.md
@echo "" >> dist/DIST_README.md
@echo "**Tip:** The folder picker loads all files (JSON, CSV, SVG, CSS) in one click. You can also load files individually using the separate upload buttons if needed." >> dist/DIST_README.md
@echo "" >> dist/DIST_README.md
@echo "See README.md and TEMPLATE_V2_GUIDE.md for detailed instructions." >> dist/DIST_README.md
@echo "" >> dist/DIST_README.md

View File

@@ -102,8 +102,9 @@ Just open `index.html` in any modern browser (Chrome, Firefox, Safari, Edge).
You can choose between:
* automatic loading from `project.json` in the project folder
* manual upload of a `project.json` file via the UI
* **Automatic loading** - When served via HTTP, the app auto-loads from `project.json` in project folders (binect/, my-project/, or example/)
* **Load Project Folder** - Click "📂 Load Project Folder" and select an entire project directory. All files (project.json, CSV, CSS, SVG) will be loaded automatically
* **Load files individually** - Upload project.json first, then manually upload CSV, CSS, and SVG files using the individual file buttons
### **3. Preview the timeline**

View File

@@ -243,6 +243,33 @@ See `TEMPLATE_V2_GUIDE.md` for comprehensive template documentation.
---
## Project Files Not Auto-Loading
### Problem Description
**Symptoms:**
- Uploaded project.json but CSV/SVG/CSS files don't load
- Have to manually select each file individually
- Timeline doesn't render after selecting project.json
### Solution
This is expected behavior due to browser security restrictions. When you upload a single project.json file, the browser doesn't allow automatic access to other files in the same directory.
**Use the Folder Picker (Recommended):**
1. Click **"📂 Load Project Folder"** (blue button at top of file manager)
2. Select the entire project folder
3. All files will be loaded automatically
This works because you're explicitly granting permission to access all files in the folder.
**Alternative:** Load files individually using the separate upload buttons for CSV, SVG, and CSS.
**For developers running from local server:** Files auto-load when served via HTTP (no manual upload needed).
---
## CSV Data Not Loading
### Problem Description

View File

@@ -115,9 +115,24 @@ You can open `index.html` directly in your browser, but due to CORS restrictions
### Loading Your Project
1. Start the application (using one of the options above)
2. Use the "Load Project" button to upload your `project.json`
3. Or manually upload individual CSV/CSS/SVG files
You have two options:
#### Option 1: Load Entire Project Folder (Recommended)
1. Click **"📂 Load Project Folder"**
2. Select your project folder (e.g., `example\` or your custom project folder)
3. All files (project.json, CSV, SVG, CSS) will be loaded automatically
4. Timeline generates immediately
This is the easiest method - one click loads everything!
#### Option 2: Load Files Individually
1. Click **"📁 Load"** next to "Project Configuration"
2. Select your `project.json` file
3. Manually upload CSV, CSS, and SVG files using the respective buttons
**Note:** Due to browser security, uploading just project.json won't automatically load the other files from the same directory. Use the folder picker (Option 1) for automatic loading.
## File Paths on Windows

103
engine.js
View File

@@ -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) => {

View File

@@ -239,6 +239,23 @@
<div id="fileManager" style="margin-bottom:16px; padding:16px; background:#f8f9fa; border:1px solid #e9ecef; border-radius:8px;">
<h3 style="margin:0 0 12px 0; font-size:14px; font-weight:600; color:#495057;">Project Files</h3>
<!-- Quick Load: Project Folder -->
<div style="margin-bottom:16px; padding:12px; background:#e7f3ff; border:1px solid #b3d9ff; border-radius:6px;">
<div style="display:flex; align-items:center; gap:12px;">
<label class="upload-btn" style="background:#0066cc; flex-shrink:0;">
<input type="file" id="folderInput" webkitdirectory directory multiple style="display:none;" />
📂 Load Project Folder
</label>
<span style="font-size:12px; color:#004080;">
Select an entire project folder to load all files automatically
</span>
</div>
</div>
<div style="margin:0 0 12px 0; text-align:center; color:#6c757d; font-size:12px;">
— or load files individually —
</div>
<div class="file-grid" style="display:grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap:12px; margin-bottom:16px;">
<div class="file-item">
<div class="file-header">