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:
@@ -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
|
||||
|
||||
```
|
||||
|
||||
6
Makefile
6
Makefile
@@ -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
|
||||
|
||||
@@ -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**
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
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) => {
|
||||
|
||||
17
index.html
17
index.html
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user