Spaces:
Running
Running
Download index.html from smithmegan/Test_1: direct link, hf CLI and curl.
- Browser
- Download file 7.37 kB
-
https://huggingface.co/spaces/smithmegan/Test_1/resolve/main/index.html
- Command line
-
hf download hf://spaces/smithmegan/Test_1/index.html
-
curl -L -o index.html https://huggingface.co/spaces/smithmegan/Test_1/resolve/main/index.html
7.37 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
| <title>AI Recommendation Engine</title> | |
| <!-- Fast CSV Parser & Markdown Renderer CDNs --> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.4.1/papaparse.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <style> | |
| :root { | |
| --primary: #4f46e5; | |
| --bg: #f9fafb; | |
| --card: #ffffff; | |
| --text: #1f2937; | |
| --border: #e5e7eb; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | |
| background-color: var(--bg); | |
| color: var(--text); | |
| max-width: 1200px; | |
| margin: 30px auto; | |
| padding: 0 20px; | |
| } | |
| /* Responsive Two-Column Layout */ | |
| .container { | |
| display: grid; | |
| grid-template-columns: 1.6fr 1fr; /* Quiz takes ~60%, Sidebar ~40% */ | |
| gap: 24px; | |
| } | |
| /* Stack on mobile screens */ | |
| @media (max-width: 768px) { | |
| .container { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| .card { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 24px; | |
| box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05); | |
| } | |
| /* Form Styles */ | |
| .form-group { | |
| margin-bottom: 16px; | |
| } | |
| label { | |
| display: block; | |
| font-weight: 600; | |
| margin-bottom: 6px; | |
| text-transform: none; | |
| } | |
| select, input[type="number"] { | |
| width: 100%; | |
| padding: 10px; | |
| border: 1px solid var(--border); | |
| border-radius: 6px; | |
| box-sizing: border-box; | |
| font-size: 14px; | |
| } | |
| button { | |
| width: 100%; | |
| background-color: var(--primary); | |
| color: white; | |
| border: none; | |
| padding: 12px; | |
| border-radius: 6px; | |
| font-size: 16px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: background 0.2s; | |
| } | |
| button:hover { | |
| background-color: #4338ca; | |
| } | |
| /* Result Box */ | |
| .result-box { | |
| margin-top: 20px; | |
| padding: 16px; | |
| background: #eef2ff; | |
| border-left: 4px solid var(--primary); | |
| border-radius: 4px; | |
| display: none; | |
| } | |
| /* Sidebar Markdown Styles */ | |
| .sidebar h1, .sidebar h2, .sidebar h3 { | |
| color: var(--primary); | |
| margin-top: 0; | |
| } | |
| .sidebar img { | |
| max-width: 100%; | |
| height: auto; | |
| margin-top: 15px; | |
| } | |
| .disclaimer { | |
| font-size: 13px; | |
| color: #6b7280; | |
| margin-top: 16px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <!-- MAIN COLUMN: Quiz Interface --> | |
| <main class="card"> | |
| <h2>😌 AI Recommendation Engine</h2> | |
| <p>Take the quiz below to get a personalized recommendation based on our dataset.</p> | |
| <div id="loading">Loading dataset...</div> | |
| <form id="quiz-form" style="display: none;"></form> | |
| <div id="result-box" class="result-box"> | |
| <h3 style="margin: 0 0 8px 0;">Your Recommendation:</h3> | |
| <p id="recommendation-text" style="font-size: 18px; font-weight: bold; margin: 0;"></p> | |
| </div> | |
| <div class="disclaimer"> | |
| ⭐ <strong>Note:</strong> Recommendations are derived using nearest-neighbor similarity matching against <code>data.csv</code> directly in your browser. | |
| </div> | |
| </main> | |
| <!-- SIDEBAR COLUMN: Project Info (renders info.md) --> | |
| <aside class="card sidebar" id="sidebar-content"> | |
| <p>Loading project details...</p> | |
| </aside> | |
| </div> | |
| <script> | |
| let csvData = []; | |
| let featureCols = []; | |
| let targetCol = ""; | |
| // 1. Fetch & Render info.md into the sidebar automatically | |
| fetch("info.md") | |
| .then(res => res.text()) | |
| .then(text => { | |
| document.getElementById("sidebar-content").innerHTML = marked.parse(text); | |
| }) | |
| .catch(() => { | |
| document.getElementById("sidebar-content").innerHTML = "<p>Could not load info.md</p>"; | |
| }); | |
| // 2. Fetch & Parse data.csv | |
| Papa.parse("data.csv", { | |
| download: true, | |
| header: true, | |
| skipEmptyLines: true, | |
| complete: function(results) { | |
| csvData = results.data; | |
| if (csvData.length === 0) return; | |
| const allCols = Object.keys(csvData[0]); | |
| const hasTimestamp = allCols[0].toLowerCase().includes("time"); | |
| const cleanCols = hasTimestamp ? allCols.slice(1) : allCols; | |
| targetCol = cleanCols[cleanCols.length - 1]; | |
| featureCols = cleanCols.slice(0, cleanCols.length - 1); | |
| buildForm(); | |
| } | |
| }); | |
| // 3. Dynamic Form Generator | |
| function buildForm() { | |
| const form = document.getElementById("quiz-form"); | |
| featureCols.forEach(col => { | |
| const group = document.createElement("div"); | |
| group.className = "form-group"; | |
| const label = document.createElement("label"); | |
| label.innerText = col; | |
| group.appendChild(label); | |
| const sampleValue = csvData[0][col]; | |
| const isNumeric = !isNaN(parseFloat(sampleValue)) && isFinite(sampleValue); | |
| if (isNumeric) { | |
| const input = document.createElement("input"); | |
| input.type = "number"; | |
| input.name = col; | |
| input.step = "any"; | |
| input.required = true; | |
| input.placeholder = `Enter ${col}`; | |
| group.appendChild(input); | |
| } else { | |
| const select = document.createElement("select"); | |
| select.name = col; | |
| select.required = true; | |
| const uniqueVals = [...new Set(csvData.map(row => row[col]))].filter(Boolean); | |
| uniqueVals.forEach(val => { | |
| const opt = document.createElement("option"); | |
| opt.value = val; | |
| opt.innerText = val; | |
| select.appendChild(opt); | |
| }); | |
| group.appendChild(select); | |
| } | |
| form.appendChild(group); | |
| }); | |
| const btn = document.createElement("button"); | |
| btn.type = "submit"; | |
| btn.innerText = "Click to see your personalized result!"; | |
| form.appendChild(btn); | |
| document.getElementById("loading").style.display = "none"; | |
| form.style.display = "block"; | |
| form.addEventListener("submit", getRecommendation); | |
| } | |
| // 4. Recommendation Logic | |
| function getRecommendation(e) { | |
| e.preventDefault(); | |
| const formData = new FormData(e.target); | |
| const userInputs = {}; | |
| featureCols.forEach(col => userInputs[col] = formData.get(col)); | |
| let minDistance = Infinity; | |
| let bestMatchRow = null; | |
| csvData.forEach(row => { | |
| let distance = 0; | |
| featureCols.forEach(col => { | |
| const userVal = userInputs[col]; | |
| const rowVal = row[col]; | |
| if (!isNaN(userVal) && !isNaN(rowVal)) { | |
| distance += Math.pow(parseFloat(userVal) - parseFloat(rowVal), 2); | |
| } else { | |
| distance += (userVal === rowVal ? 0 : 1); | |
| } | |
| }); | |
| if (distance < minDistance) { | |
| minDistance = distance; | |
| bestMatchRow = row; | |
| } | |
| }); | |
| const resultBox = document.getElementById("result-box"); | |
| const resultText = document.getElementById("recommendation-text"); | |
| resultText.innerText = bestMatchRow[targetCol]; | |
| resultBox.style.display = "block"; | |
| } | |
| </script> | |
| </body> | |
| </html> |