From cefbf96a822b86b185f7272ba8e32c231d8e67a2 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 23 Jan 2026 16:34:35 +0100 Subject: [PATCH] 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 --- CLAUDE.md | 7 +++ Makefile | 6 ++- README.md | 5 ++- TROUBLESHOOTING.md | 27 ++++++++++++ WINDOWS_USAGE.md | 21 +++++++-- engine.js | 103 +++++++++++++++++++++++++++++++++++++++++++++ index.html | 17 ++++++++ 7 files changed, 180 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f8ac9ec..74e40b8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 ``` diff --git a/Makefile b/Makefile index dc2d5c2..6c9afa7 100644 --- a/Makefile +++ b/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 diff --git a/README.md b/README.md index f5a5770..edf6912 100644 --- a/README.md +++ b/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** diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index f22bc7b..faede74 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -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 diff --git a/WINDOWS_USAGE.md b/WINDOWS_USAGE.md index 4d1b7b6..f8a81c2 100644 --- a/WINDOWS_USAGE.md +++ b/WINDOWS_USAGE.md @@ -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 diff --git a/engine.js b/engine.js index f2d92be..8ab5fd1 100644 --- a/engine.js +++ b/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) => { diff --git a/index.html b/index.html index 3cfcbdf..c64809d 100644 --- a/index.html +++ b/index.html @@ -239,6 +239,23 @@

Project Files

+ +
+
+ + + Select an entire project folder to load all files automatically + +
+
+ +
+ — or load files individually — +
+