From fc1e73d874039de63a40e296c969b8ede9a25b64 Mon Sep 17 00:00:00 2001 From: Yggdrasil75 Date: Thu, 6 Nov 2025 08:18:49 -0500 Subject: [PATCH] split files --- util/simple_httpserver.hpp | 144 +++++++++---------------------------- web/index.html | 23 ++++++ web/script.js | 43 +++++++++++ web/style.css | 109 ++++++++++++++++++++++++++++ 4 files changed, 209 insertions(+), 110 deletions(-) create mode 100644 web/index.html create mode 100644 web/script.js create mode 100644 web/style.css diff --git a/util/simple_httpserver.hpp b/util/simple_httpserver.hpp index 458e351..1cd93ac 100644 --- a/util/simple_httpserver.hpp +++ b/util/simple_httpserver.hpp @@ -30,6 +30,7 @@ private: int serverSocket; int port; bool running; + std::string webRoot; // Function to convert hex color string to Vec4 Vec4 hexToVec4(const std::string& hex) { @@ -87,9 +88,8 @@ private: // Render to RGB image std::vector imageData = grid.renderToRGB(width, height); - // Save as BMP + // Save as JXL return JXLWriter::saveJXL(filename, imageData, width, height); - // return BMPWriter::saveBMP(filename, imageData, width, height); } // Read file content @@ -104,6 +104,18 @@ private: return ss.str(); } + // Get content type based on file extension + std::string getContentType(const std::string& filename) { + if (filename.find(".html") != std::string::npos) return "text/html"; + if (filename.find(".css") != std::string::npos) return "text/css"; + if (filename.find(".js") != std::string::npos) return "application/javascript"; + if (filename.find(".jxl") != std::string::npos) return "image/jxl"; + if (filename.find(".png") != std::string::npos) return "image/png"; + if (filename.find(".jpg") != std::string::npos || filename.find(".jpeg") != std::string::npos) return "image/jpeg"; + if (filename.find(".json") != std::string::npos) return "application/json"; + return "text/plain"; + } + // Send HTTP response void sendResponse(int clientSocket, const std::string& content, const std::string& contentType = "text/html", int statusCode = 200) { std::string statusText = "OK"; @@ -121,9 +133,22 @@ private: std::string responseStr = response.str(); send(clientSocket, responseStr.c_str(), responseStr.length(), 0); } + + // Serve static file + void serveStaticFile(int clientSocket, const std::string& filepath) { + std::string fullPath = webRoot + "/" + filepath; + std::string content = readFile(fullPath); + + if (!content.empty()) { + sendResponse(clientSocket, content, getContentType(filepath)); + } else { + sendResponse(clientSocket, "404 Not Found: " + filepath, "text/plain", 404); + } + } public: - SimpleHTTPServer(int port = 8080) : port(port), serverSocket(-1), running(false) {} + SimpleHTTPServer(int port = 8080, const std::string& webRoot = "web") + : port(port), serverSocket(-1), running(false), webRoot(webRoot) {} ~SimpleHTTPServer() { stop(); @@ -171,6 +196,7 @@ public: running = true; std::cout << "Server started on port " << port << std::endl; + std::cout << "Web root: " << webRoot << std::endl; return true; } @@ -213,7 +239,11 @@ public: // Handle different routes if (request.find("GET / ") != std::string::npos || request.find("GET /index.html") != std::string::npos) { - sendResponse(clientSocket, getHTML()); + serveStaticFile(clientSocket, "index.html"); + } else if (request.find("GET /style.css") != std::string::npos) { + serveStaticFile(clientSocket, "style.css"); + } else if (request.find("GET /script.js") != std::string::npos) { + serveStaticFile(clientSocket, "script.js"); } else if (request.find("GET /gradient.jxl") != std::string::npos) { // Generate and serve the gradient image if (generateGradientImage("output/gradient.jxl")) { @@ -248,112 +278,6 @@ public: std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } - - std::string getHTML() { - return R"( - - - - Dynamic Gradient Generator - - - -
- -
- Dynamic Gradient -
-
- - - - -)"; - } }; #endif \ No newline at end of file diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..5bae13a --- /dev/null +++ b/web/index.html @@ -0,0 +1,23 @@ + + + + Dynamic Gradient Generator + + + +
+

Dynamic Gradient Generator

+ +
+ + +
+ +
+ Dynamic Gradient +
+ + + + + \ No newline at end of file diff --git a/web/script.js b/web/script.js new file mode 100644 index 0000000..aee9cc5 --- /dev/null +++ b/web/script.js @@ -0,0 +1,43 @@ +let autoRefreshInterval = null; + +function refreshImage() { + const img = document.getElementById('gradientImage'); + img.classList.add('loading'); + + const timestamp = new Date().getTime(); + img.src = 'gradient.jxl?' + timestamp; + + img.onload = function() { + img.classList.remove('loading'); + updateStatus('Image refreshed at ' + new Date().toLocaleTimeString()); + }; + + img.onerror = function() { + img.classList.remove('loading'); + updateStatus('Error loading image', 'error'); + }; +} + + +function toggleAutoRefresh() { + const button = document.getElementById('autoRefreshBtn'); + + if (autoRefreshInterval) { + clearInterval(autoRefreshInterval); + autoRefreshInterval = null; + button.textContent = 'Start Auto-Refresh (30s)'; + button.classList.remove('danger'); + updateStatus('Auto-refresh stopped'); + } else { + autoRefreshInterval = setInterval(refreshImage, 30000); + button.textContent = 'Stop Auto-Refresh'; + button.classList.add('danger'); + updateStatus('Auto-refresh started (30s interval)'); + } +} + + +// Auto-refresh when page loads +document.addEventListener('DOMContentLoaded', function() { + refreshImage(); +}); \ No newline at end of file diff --git a/web/style.css b/web/style.css new file mode 100644 index 0000000..b124dcb --- /dev/null +++ b/web/style.css @@ -0,0 +1,109 @@ +body { + font-family: 'Arial', sans-serif; + margin: 0; + padding: 20px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + text-align: center; + min-height: 100vh; +} + +.container { + max-width: 800px; + margin: 0 auto; + background: rgba(255, 255, 255, 0.1); + padding: 30px; + border-radius: 15px; + backdrop-filter: blur(10px); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); +} + +h1 { + margin-bottom: 20px; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); +} + +.image-container { + margin: 20px 0; + padding: 20px; + background: rgba(255, 255, 255, 0.2); + border-radius: 10px; +} + +img { + max-width: 100%; + height: auto; + border-radius: 8px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3); + transition: opacity 0.3s ease; +} + +img.loading { + opacity: 0.7; +} + +.controls { + margin: 20px 0; +} + +button { + background: #4CAF50; + color: white; + border: none; + padding: 12px 24px; + border-radius: 6px; + cursor: pointer; + font-size: 16px; + margin: 5px; + transition: background 0.3s, transform 0.2s; +} + +button:hover { + background: #45a049; + transform: translateY(-2px); +} + +button:active { + transform: translateY(0); +} + +button.secondary { + background: #2196F3; +} + +button.secondary:hover { + background: #1976D2; +} + +button.danger { + background: #f44336; +} + +button.danger:hover { + background: #d32f2f; +} + +.info { + margin-top: 20px; + padding: 15px; + background: rgba(255, 255, 255, 0.15); + border-radius: 8px; + text-align: left; +} + +.info h3 { + margin-top: 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.3); + padding-bottom: 8px; +} + +.info ul { + padding-left: 20px; +} + +.status { + margin: 10px 0; + padding: 10px; + border-radius: 5px; + background: rgba(255, 255, 255, 0.2); +} \ No newline at end of file