From 0c0daf947e3c5a7827bcd3dd2fe442faa071e364 Mon Sep 17 00:00:00 2001 From: Paul Bailey Date: Mon, 3 Aug 2026 23:50:06 +0100 Subject: [PATCH] feat: deselect all button, static serving for production, cloudflare tunnel setup --- api/package-lock.json | 1 + api/package.json | 1 + api/server.js | 12 +++++++++++- index.html | 1 + src/main.js | 11 ++++++++++- src/style.css | 2 ++ 6 files changed, 26 insertions(+), 2 deletions(-) diff --git a/api/package-lock.json b/api/package-lock.json index c3ffc9b..38d013a 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -14,6 +14,7 @@ "express": "4.21.2", "jsonwebtoken": "^9.0.2", "pg": "^8.13.1", + "serve-static": "^1.16.2", "sql.js": "^1.11.0" } }, diff --git a/api/package.json b/api/package.json index 7fc37a3..760e20d 100644 --- a/api/package.json +++ b/api/package.json @@ -12,6 +12,7 @@ "express": "4.21.2", "jsonwebtoken": "^9.0.2", "pg": "^8.13.1", + "serve-static": "^1.16.2", "sql.js": "^1.11.0" } } diff --git a/api/server.js b/api/server.js index 0ac385d..03d78c2 100644 --- a/api/server.js +++ b/api/server.js @@ -7,11 +7,13 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import { config } from 'dotenv'; +import serveStatic from 'serve-static'; config(); const __dirname = dirname(fileURLToPath(import.meta.url)); const DATA_DIR = join(__dirname, 'data'); +const DIST_DIR = join(__dirname, '..', 'dist'); mkdirSync(DATA_DIR, { recursive: true }); const app = express(); @@ -240,6 +242,14 @@ app.get('/api/analytics/stats', (req, res) => { }); }); +// ============ STATIC FILES + SPA FALLBACK ============ +app.use(serveStatic(DIST_DIR)); +app.get('*', (req, res) => { + if (!req.path.startsWith('/api')) { + res.sendFile(join(DIST_DIR, 'index.html')); + } +}); + // ============ START ============ const PORT = process.env.PORT || 3052; -app.listen(PORT, () => { console.log(`StackPilot API running on port ${PORT}`); }); +app.listen(PORT, () => { console.log(`StackPilot running on port ${PORT}`); }); diff --git a/index.html b/index.html index d051048..decf62c 100644 --- a/index.html +++ b/index.html @@ -76,6 +76,7 @@ +
diff --git a/src/main.js b/src/main.js index e449dc3..d3ad181 100644 --- a/src/main.js +++ b/src/main.js @@ -419,7 +419,7 @@ function initSearch() { // Presets function initPresets() { - document.querySelectorAll('.preset-btn').forEach(btn => { + document.querySelectorAll('.preset-btn[data-preset]').forEach(btn => { btn.addEventListener('click', () => { const preset = presets[btn.dataset.preset]; if (!preset) return; @@ -433,6 +433,15 @@ function initPresets() { updateUrlState(); }); }); + + document.getElementById('deselect-all-btn').addEventListener('click', () => { + state.selected.clear(); + renderAppGrid(); + checkDependencies(); + checkPortConflicts(); + updatePreview(); + updateUrlState(); + }); } // Mode switching (builder vs analyser vs gallery) diff --git a/src/style.css b/src/style.css index 439d4d1..c10c7c4 100644 --- a/src/style.css +++ b/src/style.css @@ -202,6 +202,8 @@ body { transition: all 0.2s ease; } .preset-btn:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-glow); } +.preset-btn.preset-clear { border-color: rgba(239,68,68,0.3); color: var(--danger); } +.preset-btn.preset-clear:hover { background: rgba(239,68,68,0.1); border-color: var(--danger); } .dependency-warnings, .port-conflicts { margin-bottom: 1rem; } .dep-warning, .port-conflict { background: rgba(245,158,11,0.06);