Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions web-app/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -2294,6 +2294,111 @@ body.sidebar-active .sidebar-dock {
opacity: 1;
}


.recent-clear-all-btn {
background: none;
border: none;
color: var(--accent);
cursor: pointer;
font-size: 0.85rem;
padding: 0 6px;
margin-left: 12px;
display: inline-flex;
align-items: center;
gap: 8px;
transition: color var(--duration-fast) ease, text-shadow var(--duration-fast) ease;
}

.recent-clear-all-btn:hover {
color: var(--accent-glow);
text-shadow: 0 0 8px var(--accent-glow);
}

.dropdown-clear-all {
padding: 12px 16px;
border-top: 1px solid var(--border);
justify-content: center;
}

.dropdown-clear-all-btn {
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
font-size: 0.8rem;
padding: 0;
display: flex;
align-items: center;
gap: 6px;
transition: color var(--duration-fast) ease;
}

.dropdown-clear-all-btn:hover {
color: var(--accent);
}

/* Confirm modal styles */
.confirm-modal-overlay {
position: fixed;
inset: 0;
display: none;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.45);
z-index: 1200;
}

.confirm-modal-overlay.active {
display: flex;
}

.confirm-modal-content {
background: var(--surface);
color: var(--text);
border-radius: 10px;
padding: 18px;
width: 320px;
max-width: calc(100% - 40px);
box-shadow: 0 8px 30px rgba(2,6,23,0.6);
text-align: left;
}

.confirm-modal-content h3 {
margin: 0 0 8px 0;
font-size: 1.05rem;
}

.confirm-modal-content p {
margin: 0 0 16px 0;
color: var(--text-secondary);
font-size: 0.95rem;
}

.confirm-modal-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}

.btn {
padding: 8px 12px;
border-radius: 6px;
border: none;
cursor: pointer;
}

.btn-primary {
background: var(--accent);
color: var(--bg-on-accent, #fff);
}

.btn-primary:hover { box-shadow: 0 0 10px var(--accent-glow); }

.btn-secondary {
background: transparent;
color: var(--text-secondary);
}

.tips-section p {
font-size: 0.85rem;
color: var(--text-secondary);
Expand Down
93 changes: 53 additions & 40 deletions web-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,33 +89,27 @@
</button>
</div>
</div>
</nav>

<!-- Mobile Sidebar Toggle -->
<button
type="button"
class="mobile-sidebar-toggle"
id="mobileSidebarToggle"
aria-label="Toggle sidebar menu"
aria-expanded="false"
>
<i class="fas fa-bars" aria-hidden="true"></i>
</button>

<!-- Sidebar -->
<aside
class="sidebar-dock"
id="mainSidebar"
aria-label="Primary Navigation"
>
<div class="sidebar-brand">
<a class="logo-wrap" href="index.html">
<div class="logo-top-row">
<div class="logo-text">
<span class="logo-py">py</span>
<span class="logo-mini">.mini</span>
<span class="logo-parens">()</span>
<span class="logo-tagline">games, math &amp; terminal toys</span>
</a>
</div>

<div class="sidebar-search">
<div class="search-box" style="position:relative">
<i class="fas fa-search" aria-hidden="true"></i>
<input aria-label="Search projects" id="searchInput" placeholder="Search projects or tags..." type="text" role="combobox" aria-autocomplete="list" aria-controls="resultsList" aria-expanded="false"/>
<div class="search-dropdown" id="searchDropdown" aria-live="polite">
<div class="dropdown-content" id="searchDropdownContent">
<div class="search-loader" id="searchLoader" style="display:none"><div class="spinner"></div></div>
<div class="recent-searches-section" id="recentSearchesSection">
<div class="dropdown-section-title">
<span>Recent</span>
<button id="clearRecentBtn" class="recent-clear-all-btn" aria-label="Clear recent searches">Clear All</button>
</div>
<div class="dropdown-list" id="recentSearchesList"></div>
</div>
<div class="results-section" id="resultsSection" style="display:none">
<div class="dropdown-section-title">Projects</div>
<div class="dropdown-list" id="resultsList" role="listbox"></div>
</div>
<span class="logo-tagline">games, math &amp; terminal toys</span>
</a>
Expand Down Expand Up @@ -1461,19 +1455,38 @@ <h2>${projectName.replace(/-/g, " ").toUpperCase()}</h2>
if (typeof window.openProjectSafe !== "function") {
window.openProjectSafe = window.openProjectModal;
}

console.log("✅ Modal fix loaded");
})();
</script>

<!-- Info Modal Popup -->
<div id="infoModalOverlay" class="info-modal-overlay">
<div class="info-modal-content">
<button class="info-modal-close" id="infoModalClose">&times;</button>
<h3 id="infoModalTitle">How to Play</h3>
<ul id="infoModalList"></ul>
<button class="info-modal-btn" id="infoModalGotIt">Got it</button>
</div>
});

// Override the existing openProjectSafe if needed
if (typeof window.openProjectSafe !== 'function') {
window.openProjectSafe = window.openProjectModal;
}

console.log('✅ Modal fix loaded');
})();
</script>

<!-- Info Modal Popup -->
<div id="infoModalOverlay" class="info-modal-overlay">
<div class="info-modal-content">
<button class="info-modal-close" id="infoModalClose">&times;</button>
<h3 id="infoModalTitle">How to Play</h3>
<ul id="infoModalList"></ul>
<button class="info-modal-btn" id="infoModalGotIt">Got it</button>
</div>
</div>

<!-- Themed Confirm Modal (used for Clear All) -->
<div id="confirmModalOverlay" class="confirm-modal-overlay" aria-hidden="true">
<div class="confirm-modal-content" role="dialog" aria-modal="true" aria-labelledby="confirmModalTitle">
<h3 id="confirmModalTitle">Confirm</h3>
<p id="confirmModalMessage"></p>
<div class="confirm-modal-actions">
<button id="confirmCancelBtn" class="btn btn-secondary">Cancel</button>
<button id="confirmOkBtn" class="btn btn-primary">Confirm</button>
</div>
</body>
</div>
</div>

</body>
</html>
94 changes: 81 additions & 13 deletions web-app/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,42 @@ function showInfoModal(title, steps) {
overlay.addEventListener("click", overlayClick);
}

// Themed confirmation modal (in-page) helper
function showConfirm(message, onConfirm, onCancel) {
var overlay = document.getElementById('confirmModalOverlay');
var msg = document.getElementById('confirmModalMessage');
var okBtn = document.getElementById('confirmOkBtn');
var cancelBtn = document.getElementById('confirmCancelBtn');
if (!overlay || !msg || !okBtn || !cancelBtn) {
// fallback to window.confirm
var ok = window.confirm(message);
if (ok && typeof onConfirm === 'function') onConfirm();
else if (!ok && typeof onCancel === 'function') onCancel();
return;
}

msg.textContent = message;
overlay.classList.add('active');

function cleanup() {
overlay.classList.remove('active');
okBtn.removeEventListener('click', okHandler);
cancelBtn.removeEventListener('click', cancelHandler);
overlay.removeEventListener('click', overlayClick);
document.removeEventListener('keydown', keyHandler);
}

function okHandler(e) { e.stopPropagation(); cleanup(); if (typeof onConfirm === 'function') onConfirm(); }
function cancelHandler(e) { e.stopPropagation(); cleanup(); if (typeof onCancel === 'function') onCancel(); }
function overlayClick(e) { if (e.target === overlay) { cancelHandler(e); } }
function keyHandler(e) { if (e.key === 'Escape') cancelHandler(e); }

okBtn.addEventListener('click', okHandler);
cancelBtn.addEventListener('click', cancelHandler);
overlay.addEventListener('click', overlayClick);
document.addEventListener('keydown', keyHandler);
}

var currentProjectName = "";

function setupModalInfoButton(projectName) {
Expand Down Expand Up @@ -743,7 +779,7 @@ document.addEventListener("DOMContentLoaded", function () {
label.addEventListener("click", function () {
syncSearchInputs(search, searchInput);
currentSearchQuery = search;
performSearch();
performSearch(true);
closeDropdown();
});

Expand All @@ -759,9 +795,27 @@ document.addEventListener("DOMContentLoaded", function () {
renderRecentSearches();
});

recentSearchesList.appendChild(item);
});
}
recentSearchesList.appendChild(item);
});

// Wire up header Clear All button (if present)
var headerClearBtn = document.getElementById('clearRecentBtn');
if (headerClearBtn) {
headerClearBtn.style.display = recentSearches.length ? 'inline-flex' : 'none';
headerClearBtn.onclick = function (e) {
e.stopPropagation();
if (!recentSearches || recentSearches.length === 0) return;
showConfirm('Clear all recent searches? This cannot be undone.', function () {
recentSearches = [];
localStorage.setItem('recentSearches', JSON.stringify(recentSearches));
renderRecentSearches();
closeDropdown();
}, function () {
// cancelled
});
};
}
}

recentSearchesSection.style.display = "block";
if (resultsSection) resultsSection.style.display = "none";
Expand Down Expand Up @@ -843,7 +897,7 @@ document.addEventListener("DOMContentLoaded", function () {
if (!searchInput) return;
searchInput.value = title;
currentSearchQuery = title.toLowerCase();
performSearch();
performSearch(true);
closeDropdown();
if (projectsSection) {
projectsSection.scrollIntoView({
Expand All @@ -853,7 +907,8 @@ document.addEventListener("DOMContentLoaded", function () {
}
}

function performSearch() {
function performSearch(commit) {
if (commit === undefined) commit = true;
var query = currentSearchQuery;
if (!query) {
applyCategoryFilter(currentCategory);
Expand All @@ -869,12 +924,14 @@ document.addEventListener("DOMContentLoaded", function () {
syncStickyTabs("all");
}

recentSearches = recentSearches.filter(function (s) {
return s !== query;
});
recentSearches.unshift(query);
recentSearches = recentSearches.slice(0, 10);
localStorage.setItem("recentSearches", JSON.stringify(recentSearches));
if (commit) {
recentSearches = recentSearches.filter(function (s) {
return s !== query;
});
recentSearches.unshift(query);
recentSearches = recentSearches.slice(0, 10);
localStorage.setItem("recentSearches", JSON.stringify(recentSearches));
}

var visibleCount = 0;
var favorites = JSON.parse(localStorage.getItem("favorites") || "[]");
Expand Down Expand Up @@ -928,7 +985,7 @@ document.addEventListener("DOMContentLoaded", function () {
currentSearchQuery = query;
if (searchLoader) searchLoader.style.display = query ? "block" : "none";
debouncedSearch(query);
performSearch();
performSearch(false);
});

input.addEventListener("focus", function () {
Expand All @@ -938,11 +995,22 @@ document.addEventListener("DOMContentLoaded", function () {
renderRecentSearches();
});

input.addEventListener("blur", function () {
if (currentSearchQuery && currentSearchQuery.trim()) {
performSearch(true);
}
});

input.addEventListener("keydown", function (e) {
if (e.key === "Escape") {
closeDropdown();
input.blur();
}
if (e.key === "Enter") {
e.preventDefault();
performSearch(true);
closeDropdown();
}
});
});
}
Expand Down
Loading