From 57a7b0e68f78c6ebbc32641da2b73f4f49731703 Mon Sep 17 00:00:00 2001 From: rattatwinko Date: Sat, 16 Aug 2025 21:44:29 +0200 Subject: [PATCH 1/2] semi working darkmode --- src/static/index.css | 169 +++++++++++++--- src/static/problem.css | 156 ++++++++++---- src/static/script.js | 402 +++++++++++++++++++++++++++++++++++++ src/static/style.css | 145 +++++++++---- src/templates/index.html | 21 +- src/templates/problem.html | 217 +++++++++++++------- src/templates/script.js | 274 ------------------------- 7 files changed, 928 insertions(+), 456 deletions(-) create mode 100644 src/static/script.js delete mode 100644 src/templates/script.js diff --git a/src/static/index.css b/src/static/index.css index fad9bcb..ad1ee83 100644 --- a/src/static/index.css +++ b/src/static/index.css @@ -1,24 +1,47 @@ :root { --bg: #f6f8fb; --card: #fff; + --text: #0f172a; --muted: #6b7280; --accent: #2563eb; + --border: #e5e7eb; + --hover: #f3f4f6; --shadow: 0 4px 12px rgba(16, 24, 40, 0.06); --radius: 8px; - --mono: 'JetBrains Mono', monospace; + --mono: "JetBrains Mono", monospace; } -* { box-sizing: border-box; margin: 0; padding: 0; } -html, body { + +/* Dark mode variables */ +html.dark { + --bg: #0f172a; + --card: #1e293b; + --text: #f1f5f9; + --muted: #94a3b8; + --accent: #3b82f6; + --border: #334155; + --hover: #334155; + --shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} +html, +body { height: 100%; } body { font-family: Inter, sans-serif; background: var(--bg); - color: #0f172a; + color: var(--text); padding: 16px; display: flex; justify-content: center; align-items: center; + transition: + background-color 0.3s ease, + color 0.3s ease; } .wrap { width: 100%; @@ -27,15 +50,49 @@ body { header { margin-bottom: 14px; } +.header-content { + display: flex; + justify-content: center; + align-items: center; + position: relative; +} header h1 { text-align: center; font-size: 1.6rem; - color: #111827; + color: var(--text); } header p { color: var(--muted); font-size: 0.9rem; } +.dark-mode-toggle { + position: absolute; + right: 0; + background: none; + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 6px 10px; + cursor: pointer; + color: var(--text); + font-size: 1.2rem; + transition: all 0.3s ease; +} +.dark-mode-toggle:hover { + background: var(--hover); + transform: scale(1.05); +} +html.dark .dark-mode-icon::before { + content: "â˜€ī¸"; +} +html:not(.dark) .dark-mode-icon::before { + content: "🌙"; +} +.dark-mode-icon { + display: inline-block; +} +.dark-mode-icon::before { + font-size: 1em; +} .content { display: grid; grid-template-columns: 1fr 1fr; @@ -59,32 +116,53 @@ header p { .search-input { flex: 1; padding: 6px 10px; - border: 1px solid #e5e7eb; + border: 1px solid var(--border); border-radius: 4px; font-size: 0.9rem; + background: var(--card); + color: var(--text); + transition: border-color 0.3s ease; +} +.search-input:focus { + outline: none; + border-color: var(--accent); } .filter-select { padding: 6px 8px; - border: 1px solid #e5e7eb; + border: 1px solid var(--border); border-radius: 4px; font-size: 0.9rem; - background: white; + background: var(--card); + color: var(--text); + transition: border-color 0.3s ease; +} +.filter-select:focus { + outline: none; + border-color: var(--accent); } /* Problems list */ .problems-list .problem-item { padding: 8px; - border-bottom: 1px solid #e5e7eb; + border-bottom: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; + transition: background-color 0.3s ease; +} +.problem-item:hover { + background: var(--hover); } .problem-item:last-child { border-bottom: none; } .problem-item a { text-decoration: none; - color: #0077ff; + color: var(--accent); font-weight: 600; + transition: color 0.3s ease; +} +.problem-item a:hover { + text-decoration: underline; } /* Difficulty badge */ .difficulty { @@ -99,14 +177,14 @@ header p { white-space: nowrap; } .difficulty[data-difficulty="easy"] { - background-color: #4CAF50; /* Green */ + background-color: #4caf50; /* Green */ } .difficulty[data-difficulty="medium"] { - background-color: #FFC107; /* Amber */ + background-color: #ffc107; /* Amber */ color: #333; } .difficulty[data-difficulty="hard"] { - background-color: #F44336; /* Red */ + background-color: #f44336; /* Red */ } /* Leaderboard */ .leaderboard-head { @@ -128,16 +206,16 @@ header p { .leaderboard-table th, .leaderboard-table td { padding: 6px 8px; - border-bottom: 1px solid #e5e7eb; + border-bottom: 1px solid var(--border); text-align: left; } .leaderboard-table th { - background: #f9fafb; + background: var(--hover); font-weight: 600; color: var(--muted); } .leaderboard-table tr:hover { - background: #f3f4f6; + background: var(--hover); } /* Sort indicators */ .sortable { @@ -179,7 +257,9 @@ header p { background: rgba(37, 99, 235, 0.15); } @media (max-width: 800px) { - .content { grid-template-columns: 1fr; } + .content { + grid-template-columns: 1fr; + } .leaderboard-controls { flex-direction: column; } @@ -187,21 +267,60 @@ header p { /* Leaderboard horizontal collapse */ #leaderboardSection { - transition: max-width 0.35s ease, opacity 0.25s ease; - overflow: hidden; - max-width: 100%; + transition: + max-width 0.35s ease, + opacity 0.25s ease; + overflow: hidden; + max-width: 100%; } #leaderboardSection.hidden { - max-width: 0; - opacity: 0; - pointer-events: none; + max-width: 0; + opacity: 0; + pointer-events: none; } #leaderboardSection.visible { - max-width: 100%; /* take full available space in grid column */ - opacity: 1; + max-width: 100%; /* take full available space in grid column */ + opacity: 1; } #rankingExplanation { transition: all 0.35s ease; } + +/* Pagination Controls */ +.pagination-controls { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 12px; + padding-top: 12px; + border-top: 1px solid var(--border); +} +.pagination-btn { + background: var(--card); + border: 1px solid var(--border); + border-radius: 4px; + padding: 6px 12px; + cursor: pointer; + color: var(--text); + font-size: 0.9rem; + transition: all 0.3s ease; +} +.pagination-btn:hover:not(:disabled) { + background: var(--hover); + border-color: var(--accent); +} +.pagination-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} +.pagination-info { + color: var(--muted); + font-size: 0.9rem; +} + +/* Hide pagination when not needed */ +.pagination-controls.hidden { + display: none; +} diff --git a/src/static/problem.css b/src/static/problem.css index 73572da..ca509b5 100644 --- a/src/static/problem.css +++ b/src/static/problem.css @@ -1,15 +1,46 @@ -body { - font-family: 'Inter', sans-serif; - margin: 0; - padding: 0; - background-color: #f9f9f9; - color: #333; - min-height: 100vh; /* allow content to grow */ - overflow-y: auto; /* allow vertical scroll */ - box-sizing: border-box; +:root { + --bg: #f9f9f9; + --card: #fff; + --text: #333; + --muted: #666; + --accent: #007bff; + --accent-hover: #0069d9; + --border: #eaeaea; + --hover: #f8f9fa; + --code-bg: #f6f8fa; + --editor-border: #ddd; } -*, *::before, *::after { +html.dark { + --bg: #0f172a; + --card: #1e293b; + --text: #f1f5f9; + --muted: #94a3b8; + --accent: #3b82f6; + --accent-hover: #2563eb; + --border: #334155; + --hover: #334155; + --code-bg: #1e293b; + --editor-border: #475569; +} + +body { + font-family: "Inter", sans-serif; + margin: 0; + padding: 0; + background-color: var(--bg); + color: var(--text); + min-height: 100vh; /* allow content to grow */ + overflow-y: auto; /* allow vertical scroll */ + box-sizing: border-box; + transition: + background-color 0.3s ease, + color 0.3s ease; +} + +*, +*::before, +*::after { box-sizing: inherit; } @@ -23,11 +54,12 @@ body { .problem-panel { flex: 1 1 400px; /* grow/shrink with base 400px */ min-width: 300px; - background: white; + background: var(--card); overflow-y: auto; padding: 20px; - border-right: 1px solid #eaeaea; + border-right: 1px solid var(--border); max-height: 100vh; + transition: background-color 0.3s ease; } .editor-container { @@ -35,14 +67,15 @@ body { min-width: 300px; display: flex; flex-direction: column; - background: white; + background: var(--card); max-height: 100vh; overflow: hidden; /* internal scroll handling */ + transition: background-color 0.3s ease; } .editor-header { padding: 15px 20px; - border-bottom: 1px solid #eaeaea; + border-bottom: 1px solid var(--border); flex-shrink: 0; } @@ -58,28 +91,65 @@ body { .problem-header { display: flex; align-items: center; + justify-content: space-between; margin-bottom: 20px; } .back-btn { background: none; - border: none; + border: 1px solid var(--border); + border-radius: 4px; cursor: pointer; font-size: 16px; - color: #666; + color: var(--muted); margin-right: 15px; - padding: 5px; + padding: 6px 10px; + transition: all 0.3s ease; } .back-btn:hover { - color: #000; + color: var(--text); + background: var(--hover); +} + +.dark-mode-toggle { + background: none; + border: 1px solid var(--border); + border-radius: 4px; + padding: 6px 10px; + cursor: pointer; + color: var(--text); + font-size: 1.2rem; + transition: all 0.3s ease; +} + +.dark-mode-toggle:hover { + background: var(--hover); + transform: scale(1.05); +} + +html.dark .dark-mode-icon::before { + content: "â˜€ī¸"; +} + +html:not(.dark) .dark-mode-icon::before { + content: "🌙"; +} + +.dark-mode-icon { + display: inline-block; +} + +.dark-mode-icon::before { + font-size: 1em; } h1 { font-size: 22px; font-weight: 600; margin: 0; - color: #1a1a1a; + color: var(--text); + flex: 1; } .problem-desc { @@ -89,19 +159,20 @@ h1 { } .problem-desc pre { - background: #f6f8fa; + background: var(--code-bg); padding: 12px; border-radius: 4px; overflow-x: auto; - font-family: 'JetBrains Mono', monospace; + font-family: "JetBrains Mono", monospace; font-size: 14px; + border: 1px solid var(--border); } .problem-desc code { - background: #f6f8fa; + background: var(--code-bg); padding: 2px 4px; border-radius: 3px; - font-family: 'JetBrains Mono', monospace; + font-family: "JetBrains Mono", monospace; font-size: 14px; } @@ -113,7 +184,7 @@ h1 { } .editor-actions button { - background-color: #007bff; + background-color: var(--accent); color: white; border: none; padding: 8px 16px; @@ -121,16 +192,17 @@ h1 { cursor: pointer; font-weight: 500; font-size: 14px; + transition: background-color 0.3s ease; } .editor-actions button:hover { - background-color: #0069d9; + background-color: var(--accent-hover); } #editor { flex: 1 1 auto; min-height: 300px; - border: 1px solid #ddd; + border: 1px solid var(--editor-border); border-radius: 4px; overflow: auto; max-height: 60vh; @@ -139,12 +211,14 @@ h1 { .result-panel { margin-top: 20px; padding: 15px; - background: #f8f9fa; + background: var(--hover); border-radius: 4px; margin-bottom: 20px; min-height: 120px; overflow-y: auto; max-height: 30vh; + border: 1px solid var(--border); + transition: background-color 0.3s ease; } .result-panel h3 { @@ -154,18 +228,19 @@ h1 { } .result-panel pre { - background: #f6f8fa; + background: var(--code-bg); padding: 12px; border-radius: 4px; overflow-x: auto; white-space: pre-wrap; - font-family: 'JetBrains Mono', monospace; + font-family: "JetBrains Mono", monospace; font-size: 14px; margin: 5px 0; + border: 1px solid var(--border); } .placeholder { - color: #999; + color: var(--muted); font-style: italic; text-align: center; padding: 20px; @@ -175,16 +250,24 @@ label { display: block; margin-bottom: 5px; font-size: 14px; - color: #666; + color: var(--muted); } input[type="text"] { width: 100%; padding: 8px; - border: 1px solid #ddd; + border: 1px solid var(--border); border-radius: 4px; margin-bottom: 15px; - font-family: 'Inter', sans-serif; + font-family: "Inter", sans-serif; + background: var(--card); + color: var(--text); + transition: border-color 0.3s ease; +} + +input[type="text"]:focus { + outline: none; + border-color: var(--accent); } /* Responsive adjustments */ @@ -194,13 +277,14 @@ input[type="text"] { height: auto; overflow-y: visible; } - .problem-panel, .editor-container { + .problem-panel, + .editor-container { flex: none; width: 100%; min-width: auto; max-height: none; border-right: none; - border-bottom: 1px solid #eaeaea; + border-bottom: 1px solid var(--border); } #editor { min-height: 400px; @@ -209,4 +293,4 @@ input[type="text"] { .result-panel { max-height: none; } -} \ No newline at end of file +} diff --git a/src/static/script.js b/src/static/script.js new file mode 100644 index 0000000..7d151de --- /dev/null +++ b/src/static/script.js @@ -0,0 +1,402 @@ +document.addEventListener("DOMContentLoaded", () => { + // Dark mode functionality + const darkModeToggle = document.getElementById("darkModeToggle"); + const html = document.documentElement; + + // Load saved dark mode preference + const savedDarkMode = localStorage.getItem("darkMode"); + if ( + savedDarkMode === "true" || + (savedDarkMode === null && + window.matchMedia("(prefers-color-scheme: dark)").matches) + ) { + html.classList.add("dark"); + } + + darkModeToggle?.addEventListener("click", () => { + html.classList.toggle("dark"); + localStorage.setItem("darkMode", html.classList.contains("dark")); + }); + + // Problem search and pagination + const problemSearch = document.getElementById("problemSearch"); + const problemsContainer = document.getElementById("problemsContainer"); + const problemsPagination = document.getElementById("problemsPagination"); + const problemsPrevBtn = document.getElementById("problemsPrevBtn"); + const problemsNextBtn = document.getElementById("problemsNextBtn"); + const problemsPaginationInfo = document.getElementById( + "problemsPaginationInfo", + ); + + let allProblemItems = []; + let filteredProblemItems = []; + let currentPage = 1; + const itemsPerPage = 10; + + // Initialize problem items + function initializeProblemItems() { + allProblemItems = Array.from( + problemsContainer?.querySelectorAll(".problem-item") || [], + ); + filteredProblemItems = [...allProblemItems]; + updatePagination(); + } + + function updatePagination() { + const totalPages = Math.ceil(filteredProblemItems.length / itemsPerPage); + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + + // Hide all items first + allProblemItems.forEach((item) => { + item.style.display = "none"; + }); + + // Show current page items + filteredProblemItems.slice(startIndex, endIndex).forEach((item) => { + item.style.display = ""; + }); + + // Update pagination controls + if (problemsPrevBtn) problemsPrevBtn.disabled = currentPage <= 1; + if (problemsNextBtn) problemsNextBtn.disabled = currentPage >= totalPages; + if (problemsPaginationInfo) { + problemsPaginationInfo.textContent = + totalPages > 0 + ? `Page ${currentPage} of ${totalPages}` + : "No problems found"; + } + + // Hide pagination if not needed + if (problemsPagination) { + problemsPagination.classList.toggle("hidden", totalPages <= 1); + } + } + + function filterProblems() { + const term = problemSearch?.value.toLowerCase().trim() || ""; + filteredProblemItems = allProblemItems.filter((item) => { + const name = item.dataset.name?.toLowerCase() || ""; + const desc = item.dataset.desc?.toLowerCase() || ""; + return !term || name.includes(term) || desc.includes(term); + }); + currentPage = 1; + updatePagination(); + } + + // Event listeners for pagination + problemsPrevBtn?.addEventListener("click", () => { + if (currentPage > 1) { + currentPage--; + updatePagination(); + } + }); + + problemsNextBtn?.addEventListener("click", () => { + const totalPages = Math.ceil(filteredProblemItems.length / itemsPerPage); + if (currentPage < totalPages) { + currentPage++; + updatePagination(); + } + }); + + problemSearch?.addEventListener("input", filterProblems); + + // Initialize problems pagination + if (problemsContainer) { + initializeProblemItems(); + } + + // Leaderboard functionality + const problemFilter = document.getElementById("problemFilter"); + const runtimeFilter = document.getElementById("runtimeFilter"); + const leaderboardBody = document.getElementById("leaderboardBody"); + const sortableHeaders = document.querySelectorAll(".sortable"); + + let currentSort = { column: "rank", direction: "asc" }; + let allRows = []; + + // Initialize rows array + function initializeRows() { + allRows = Array.from(leaderboardBody.querySelectorAll("tr")).map((row) => { + return { + element: row, + user: row.dataset.user || "", + problem: row.dataset.problem || "", + runtime: parseFloat(row.dataset.runtime) || 0, + memory: parseFloat(row.dataset.memory) || 0, + timestamp: new Date(row.dataset.timestamp || Date.now()).getTime(), + language: row.dataset.language || "", + originalIndex: Array.from(leaderboardBody.children).indexOf(row), + }; + }); + } + + function updateRankClasses() { + const visibleRows = allRows.filter( + (row) => row.element.style.display !== "none", + ); + visibleRows.forEach((rowData, index) => { + const rank = index + 1; + const row = rowData.element; + + // Update rank cell + const rankCell = row.cells[0]; + if (rankCell) rankCell.textContent = rank; + + // Update rank classes + row.className = row.className.replace(/\brank-\d+\b/g, ""); + if (rank === 1) row.classList.add("rank-1"); + else if (rank <= 3) row.classList.add("rank-top3"); + }); + } + + function calculateOverallRanking() { + const visibleRows = allRows.filter( + (row) => row.element.style.display !== "none", + ); + + if (visibleRows.length === 0) return; + + // Group submissions by problem to find the best performance for each + const problemBests = {}; + + visibleRows.forEach((rowData) => { + const problem = rowData.problem; + if (!problemBests[problem]) { + problemBests[problem] = { + bestRuntime: Infinity, + bestMemory: Infinity, + }; + } + + problemBests[problem].bestRuntime = Math.min( + problemBests[problem].bestRuntime, + rowData.runtime, + ); + problemBests[problem].bestMemory = Math.min( + problemBests[problem].bestMemory, + rowData.memory, + ); + }); + + // Calculate normalized scores for each submission + visibleRows.forEach((rowData) => { + const problemBest = problemBests[rowData.problem]; + + // Prevent division by zero + const runtimeScore = + problemBest.bestRuntime > 0 + ? rowData.runtime / problemBest.bestRuntime + : 1; + const memoryScore = + problemBest.bestMemory > 0 + ? rowData.memory / problemBest.bestMemory + : 1; + + // Weighted overall score (70% runtime, 30% memory) + rowData.overallScore = runtimeScore * 0.7 + memoryScore * 0.3; + }); + + // Sort by overall score (lower is better), then by timestamp (earlier is better for ties) + visibleRows.sort((a, b) => { + const scoreDiff = a.overallScore - b.overallScore; + if (Math.abs(scoreDiff) > 0.000001) return scoreDiff; // Use small epsilon for float comparison + + // If scores are essentially equal, prefer earlier submission + return a.timestamp - b.timestamp; + }); + + // Reorder DOM elements and update ranks + visibleRows.forEach((rowData, index) => { + leaderboardBody.appendChild(rowData.element); + }); + + updateRankClasses(); + } + + function filterLeaderboard() { + const problemTerm = (problemFilter?.value || "").toLowerCase().trim(); + const runtimeType = runtimeFilter?.value || "all"; + + // Reset all rows to visible first + allRows.forEach((rowData) => { + rowData.element.style.display = ""; + }); + + // Apply problem filter + if (problemTerm) { + allRows.forEach((rowData) => { + const problemMatch = rowData.problem + .toLowerCase() + .includes(problemTerm); + if (!problemMatch) { + rowData.element.style.display = "none"; + } + }); + } + + // Apply runtime filter (best/worst per user per problem) + if (runtimeType === "best" || runtimeType === "worst") { + const userProblemGroups = {}; + + // Group by user + problem combination + allRows.forEach((rowData) => { + if (rowData.element.style.display === "none") return; + + const key = `${rowData.user}::${rowData.problem}`; + if (!userProblemGroups[key]) { + userProblemGroups[key] = []; + } + userProblemGroups[key].push(rowData); + }); + + // Hide all except best/worst for each user-problem combination + Object.values(userProblemGroups).forEach((group) => { + if (group.length <= 1) return; + + // Sort by runtime + group.sort((a, b) => a.runtime - b.runtime); + + const keepIndex = runtimeType === "best" ? 0 : group.length - 1; + group.forEach((rowData, index) => { + if (index !== keepIndex) { + rowData.element.style.display = "none"; + } + }); + }); + } + + calculateOverallRanking(); + } + + function getCellValue(rowData, column) { + switch (column) { + case "rank": + return parseInt(rowData.element.cells[0]?.textContent) || 0; + case "user": + return rowData.user.toLowerCase(); + case "problem": + return rowData.problem.toLowerCase(); + case "runtime": + return rowData.runtime; + case "memory": + return rowData.memory; + case "timestamp": + return rowData.timestamp; + case "language": + return rowData.language.toLowerCase(); + default: + return ""; + } + } + + function sortLeaderboard(column, direction) { + if (column === "rank") { + calculateOverallRanking(); + return; + } + + const visibleRows = allRows.filter( + (row) => row.element.style.display !== "none", + ); + + visibleRows.sort((a, b) => { + const valueA = getCellValue(a, column); + const valueB = getCellValue(b, column); + + let comparison = 0; + if (typeof valueA === "number" && typeof valueB === "number") { + comparison = valueA - valueB; + } else { + comparison = valueA < valueB ? -1 : valueA > valueB ? 1 : 0; + } + + return direction === "asc" ? comparison : -comparison; + }); + + // Reorder DOM elements + visibleRows.forEach((rowData) => { + leaderboardBody.appendChild(rowData.element); + }); + + updateRankClasses(); + } + + // Event listeners for sorting + sortableHeaders.forEach((header) => { + header.addEventListener("click", () => { + const column = header.dataset.sort; + if (!column) return; + + // Remove sorting classes from all headers + sortableHeaders.forEach((h) => + h.classList.remove("sort-asc", "sort-desc"), + ); + + // Toggle sort direction + if (currentSort.column === column) { + currentSort.direction = + currentSort.direction === "asc" ? "desc" : "asc"; + } else { + currentSort.column = column; + currentSort.direction = "asc"; + } + + // Add sorting class to current header + header.classList.add(`sort-${currentSort.direction}`); + + sortLeaderboard(column, currentSort.direction); + }); + }); + + // Filter event listeners + problemFilter?.addEventListener("input", filterLeaderboard); + runtimeFilter?.addEventListener("change", filterLeaderboard); + + // Rank info popout + const rankInfoBtn = document.getElementById("rankInfoBtn"); + const rankingExplanation = document.getElementById("rankingExplanation"); + + rankInfoBtn?.addEventListener("click", (e) => { + e.preventDefault(); + rankingExplanation?.classList.toggle("active"); + rankInfoBtn?.classList.toggle("active"); + }); + + // Close ranking explanation when clicking outside + document.addEventListener("click", (e) => { + if ( + rankingExplanation?.classList.contains("active") && + !rankingExplanation.contains(e.target) && + !rankInfoBtn?.contains(e.target) + ) { + rankingExplanation.classList.remove("active"); + rankInfoBtn?.classList.remove("active"); + } + }); + + // Initialize everything + if (leaderboardBody && leaderboardBody.children.length > 0) { + initializeRows(); + calculateOverallRanking(); + + // Set initial sort indicator + const defaultHeader = document.querySelector('[data-sort="rank"]'); + if (defaultHeader) { + defaultHeader.classList.add("sort-asc"); + } + } + + // Apply dark mode to dynamically created elements + function applyDarkModeToElements() { + const isDark = html.classList.contains("dark"); + // Any additional dark mode styling for dynamically created elements can go here + } + + // Watch for dark mode changes + new MutationObserver(applyDarkModeToElements).observe(html, { + attributes: true, + attributeFilter: ["class"], + }); +}); diff --git a/src/static/style.css b/src/static/style.css index b576b4a..1bed033 100644 --- a/src/static/style.css +++ b/src/static/style.css @@ -1,3 +1,49 @@ +:root { + --bg: #f8f9fa; + --card: #fff; + --text: #333; + --heading: #2c3e50; + --heading-secondary: #34495e; + --accent: #3498db; + --accent-hover: #2980b9; + --success: #27ae60; + --success-hover: #229954; + --error: #e74c3c; + --muted: #6c757d; + --muted-hover: #5a6268; + --border: #ddd; + --code-bg: #f4f4f4; + --success-bg: #d4edda; + --success-text: #155724; + --error-bg: #f8d7da; + --error-text: #721c24; + --hover-bg: #e3f2fd; + --shadow: 0 2px 10px rgba(0, 0, 0, 0.1); +} + +html.dark { + --bg: #0f172a; + --card: #1e293b; + --text: #f1f5f9; + --heading: #3b82f6; + --heading-secondary: #94a3b8; + --accent: #3b82f6; + --accent-hover: #2563eb; + --success: #22c55e; + --success-hover: #16a34a; + --error: #ef4444; + --muted: #64748b; + --muted-hover: #475569; + --border: #334155; + --code-bg: #1e293b; + --success-bg: #065f46; + --success-text: #d1fae5; + --error-bg: #7f1d1d; + --error-text: #fecaca; + --hover-bg: #1e40af; + --shadow: 0 2px 10px rgba(0, 0, 0, 0.3); +} + /* Reset and base styles */ * { margin: 0; @@ -6,39 +52,43 @@ } body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; line-height: 1.6; - color: #333; - background-color: #f8f9fa; + color: var(--text); + background-color: var(--bg); padding: 20px; max-width: 1200px; margin: 0 auto; + transition: + background-color 0.3s ease, + color 0.3s ease; } /* Main heading */ h1 { - color: #2c3e50; + color: var(--heading); margin-bottom: -10px; padding-bottom: 3px; - border-bottom: 3px solid #3498db; + border-bottom: 3px solid var(--accent); font-size: 2.2em; } h2 { - color: #34495e; + color: var(--heading-secondary); margin: 30px 0 20px 0; font-size: 1.5em; } h3 { - color: #34495e; + color: var(--heading-secondary); margin: 25px 0 15px 0; font-size: 1.3em; } /* Links and buttons */ a { - color: #3498db; + color: var(--accent); text-decoration: none; padding: 8px 16px; border-radius: 5px; @@ -46,13 +96,13 @@ a { } a:hover { - background-color: #e3f2fd; + background-color: var(--hover-bg); text-decoration: none; } /* Primary action link (Submit New Problem) */ a[href="/problem/new"] { - background-color: #3498db; + background-color: var(--accent); color: white; font-weight: 600; margin-bottom: 30px; @@ -62,22 +112,23 @@ a[href="/problem/new"] { } a[href="/problem/new"]:hover { - background-color: #2980b9; + background-color: var(--accent-hover); } /* Problem list */ ul { list-style: none; - background: white; + background: var(--card); border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); + box-shadow: var(--shadow); padding: 25px; margin: 20px 0; + transition: background-color 0.3s ease; } li { padding: 15px 0; - border-bottom: 1px solid #eee; + border-bottom: 1px solid var(--border); } li:last-child { @@ -93,7 +144,7 @@ li a { } li a:hover { - background-color: #f8f9fa; + background-color: var(--hover-bg); transform: translateX(5px); transition: all 0.2s ease; } @@ -107,7 +158,7 @@ li a:hover { } .back-btn { - background-color: #95a5a6; + background-color: var(--muted); color: white; border: none; padding: 10px 20px; @@ -119,30 +170,32 @@ li a:hover { } .back-btn:hover { - background-color: #7f8c8d; + background-color: var(--muted-hover); } .problem-desc { - background: white; + background: var(--card); padding: 30px; border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); + box-shadow: var(--shadow); margin-bottom: 30px; font-size: 1.1em; line-height: 1.7; + transition: background-color 0.3s ease; } /* Editor section */ .editor-section { - background: white; + background: var(--card); padding: 30px; border-radius: 8px; - box-shadow: 0 2px 10px rgba(0,0,0,0.1); + box-shadow: var(--shadow); margin-bottom: 30px; + transition: background-color 0.3s ease; } #editor { - border: 2px solid #ddd; + border: 2px solid var(--border); border-radius: 8px; margin: 20px 0; height: 400px; @@ -155,7 +208,7 @@ li a:hover { } form button[type="submit"] { - background-color: #27ae60; + background-color: var(--success); color: white; border: none; padding: 12px 30px; @@ -167,50 +220,52 @@ form button[type="submit"] { } form button[type="submit"]:hover { - background-color: #229954; + background-color: var(--success-hover); } /* Results section */ b { - color: #2c3e50; + color: var(--heading); display: inline-block; margin: 10px 0 5px 0; } pre { - background-color: #f4f4f4; + background-color: var(--code-bg); padding: 20px; border-radius: 6px; - border-left: 4px solid #3498db; + border-left: 4px solid var(--accent); margin: 10px 0 20px 0; overflow-x: auto; - font-family: 'JetBrains Mono', 'Courier New', monospace; + font-family: "JetBrains Mono", "Courier New", monospace; font-size: 14px; line-height: 1.4; + border: 1px solid var(--border); + transition: background-color 0.3s ease; } pre[style*="color:red"] { - border-left-color: #e74c3c; - background-color: #fdf2f2; + border-left-color: var(--error); + background-color: var(--error-bg); } /* Status messages */ p[style*="color:green"] { - background-color: #d4edda; - color: #155724; + background-color: var(--success-bg); + color: var(--success-text); padding: 15px 20px; border-radius: 6px; - border-left: 4px solid #27ae60; + border-left: 4px solid var(--success); margin: 20px 0; font-weight: 600; } p[style*="color:red"] { - background-color: #f8d7da; - color: #721c24; + background-color: var(--error-bg); + color: var(--error-text); padding: 15px 20px; border-radius: 6px; - border-left: 4px solid #e74c3c; + border-left: 4px solid var(--error); margin: 20px 0; font-weight: 600; } @@ -219,7 +274,7 @@ p[style*="color:red"] { a[href="/"] { display: inline-block; margin-top: 30px; - background-color: #6c757d; + background-color: var(--muted); color: white; padding: 10px 20px; border-radius: 6px; @@ -227,7 +282,7 @@ a[href="/"] { } a[href="/"]:hover { - background-color: #5a6268; + background-color: var(--muted-hover); } /* Responsive design */ @@ -235,25 +290,27 @@ a[href="/"]:hover { body { padding: 15px; } - + .problem-header { flex-direction: column; align-items: flex-start; gap: 15px; } - + h1 { font-size: 1.8em; } - - .problem-desc, .editor-section, ul { + + .problem-desc, + .editor-section, + ul { padding: 20px; } - + #editor { height: 300px; } - + .editor-actions { text-align: center; } diff --git a/src/templates/index.html b/src/templates/index.html index 14dec25..0bd6cf8 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -1,11 +1,12 @@ - + Quick Problem Platform + - - -
-
-
-

Quick Problem Platform

- -
-
-
- -
-
- -
-

Problems

-
- {% for folder, description, test_code, difficulty in problems %} -
- {{ folder.replace('_',' ').title() }} - {{ difficulty }} -
- {% else %} -
No problems yet.
- {% endfor %} -
-
- - Page 1 of 1 - -
-
- - -
-
-

Leaderboard - â„šī¸ -

-
-
- - -
-
- - - - - - - - - - - - - - {% for entry in leaderboard %} - - - - - - - - - - {% else %} - - {% endfor %} - -
RankUserProblemRuntime (s)Memory (KB)LineTimestamp
{{ loop.index }}{{ entry[0] }} - - {{ problem_titles.get(entry[1], 'Unknown') }} - - {{ '%.4f'|format(entry[2]) }}{{ entry[3] }}{{ entry[4] if entry[4] else '-' }}{{ entry[5] }}
No leaderboard entries yet.
-
-
- - -
-

How Ranking Works

-

The leaderboard uses a weighted scoring system to determine overall rank:

-
    -
  • Runtime: How fast the solution runs (lower is better).
  • -
  • Memory Usage: How much memory the solution uses (lower is better).
  • -
-

Overall score is calculated as:

-
- - runtimeScore = yourRuntime / bestRuntime
- memoryScore = yourMemory / bestMemory
- overallScore = runtimeScore × 0.7 + memoryScore × 0.3 -
-
-

Lower overall scores are better. If scores are equal, earlier submission ranks higher.

-
-
-
- - - + + + + + +Quick Problem Platform + + + + + + +
+
+
+

Quick Problem Platform

+ +
+
+
+ +
+
+ +
+

Problems

+
+ {% for folder, description, test_code, difficulty in problems %} +
+ {{ folder.replace('_',' ').title() }} + {{ difficulty }} +
+ {% else %} +
No problems yet.
+ {% endfor %} +
+
+ + Page 1 of 1 + +
+
+ + +
+
+

Leaderboard + â„šī¸ +

+
+
+ + +
+
+ + + + + + + + + + + + + + {% for entry in leaderboard %} + + + + + + + + + + {% else %} + + {% endfor %} + +
RankUserProblemRuntime (s)Memory (KB)LineTimestamp
{{ loop.index }}{{ entry[0] }} + + {{ problem_titles.get(entry[1], 'Unknown') }} + + {{ '%.4f'|format(entry[2]) }}{{ entry[3] }}{{ entry[4] if entry[4] else '-' }}{{ entry[5] }}
No leaderboard entries yet.
+
+
+ + +
+

How Ranking Works

+

The leaderboard uses a weighted scoring system to determine overall rank:

+
    +
  • Runtime: How fast the solution runs (lower is better).
  • +
  • Memory Usage: How much memory the solution uses (lower is better).
  • +
+

Overall score is calculated as:

+
+ + runtimeScore = yourRuntime / bestRuntime
+ memoryScore = yourMemory / bestMemory
+ overallScore = runtimeScore × 0.7 + memoryScore × 0.3 +
+
+

Lower overall scores are better. If scores are equal, earlier submission ranks higher.

+
+
+
+ + + diff --git a/src/templates/problem.html b/src/templates/problem.html index 9424728..46d79ca 100644 --- a/src/templates/problem.html +++ b/src/templates/problem.html @@ -1,158 +1,158 @@ - - - - - - {{ problem.title }} - Coding Problem - - - - - - - - -
-
-
- -

{{ problem.title }}

- -
-
- {{ problem.description | safe | markdown }} -
-
- -
-
-

- Submit Your Solution (Python) -

-
-
-
- - -
- -
- -
-
- -
-

Result

- {% if result %} -

- Runtime: {{ '%.4f'|format(result.runtime) }} - seconds -

-

Output:

-
{{ result.output }}
- {% if result.error %} -

Error:

-
{{ result.error }}
- {% endif %} {% else %} -
- Your code execution results will appear here -
- {% endif %} -
-
-
-
- - - - - + + + + + + {{ problem.title }} - Coding Problem + + + + + + + + +
+
+
+ +

{{ problem.title }}

+ +
+
+ {{ problem.description | safe | markdown }} +
+
+ +
+
+

+ Submit Your Solution (Python) +

+
+
+
+ + +
+ +
+ +
+
+ +
+

Result

+ {% if result %} +

+ Runtime: {{ '%.4f'|format(result.runtime) }} + seconds +

+

Output:

+
{{ result.output }}
+ {% if result.error %} +

Error:

+
{{ result.error }}
+ {% endif %} {% else %} +
+ Your code execution results will appear here +
+ {% endif %} +
+
+
+
+ + + + +