diff --git a/app.js b/app.js index 8ea6fc2e..62f97ed3 100644 --- a/app.js +++ b/app.js @@ -2032,6 +2032,7 @@ function setupControls() { document.getElementById('atlas-toggle-btn').addEventListener('click', openPortalAtlas); document.getElementById('atlas-close-btn').addEventListener('click', closePortalAtlas); + initAtlasControls(); } function sendChatMessage(overrideText = null) { @@ -2815,58 +2816,142 @@ function closeVisionOverlay() { document.getElementById('vision-overlay').style.display = 'none'; } -// ═══ PORTAL ATLAS ═══ +// ═══ PORTAL ATLAS / WORLD DIRECTORY ═══ +let atlasActiveFilter = 'all'; +let atlasSearchQuery = ''; + function openPortalAtlas() { atlasOverlayActive = true; document.getElementById('atlas-overlay').style.display = 'flex'; populateAtlas(); + // Focus search input + setTimeout(() => document.getElementById('atlas-search')?.focus(), 100); } function closePortalAtlas() { atlasOverlayActive = false; document.getElementById('atlas-overlay').style.display = 'none'; + atlasSearchQuery = ''; + atlasActiveFilter = 'all'; +} + +function initAtlasControls() { + const searchInput = document.getElementById('atlas-search'); + if (searchInput) { + searchInput.addEventListener('input', (e) => { + atlasSearchQuery = e.target.value.toLowerCase().trim(); + populateAtlas(); + }); + } + + const filterBtns = document.querySelectorAll('.atlas-filter-btn'); + filterBtns.forEach(btn => { + btn.addEventListener('click', () => { + filterBtns.forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + atlasActiveFilter = btn.dataset.filter; + populateAtlas(); + }); + }); +} + +function matchesAtlasFilter(config) { + if (atlasActiveFilter === 'all') return true; + if (atlasActiveFilter === 'harness') return (config.portal_type || 'harness') === 'harness' || !config.portal_type; + if (atlasActiveFilter === 'game-world') return config.portal_type === 'game-world'; + return config.status === atlasActiveFilter; +} + +function matchesAtlasSearch(config) { + if (!atlasSearchQuery) return true; + const haystack = [config.name, config.description, config.id, + config.world_category, config.portal_type, config.destination?.type] + .filter(Boolean).join(' ').toLowerCase(); + return haystack.includes(atlasSearchQuery); } function populateAtlas() { const grid = document.getElementById('atlas-grid'); grid.innerHTML = ''; - + let onlineCount = 0; let standbyCount = 0; - + let downloadedCount = 0; + let visibleCount = 0; + portals.forEach(portal => { const config = portal.config; if (config.status === 'online') onlineCount++; if (config.status === 'standby') standbyCount++; - + if (config.status === 'downloaded') downloadedCount++; + + if (!matchesAtlasFilter(config) || !matchesAtlasSearch(config)) return; + visibleCount++; + const card = document.createElement('div'); card.className = 'atlas-card'; card.style.setProperty('--portal-color', config.color); - + const statusClass = `status-${config.status || 'online'}`; - + const statusLabel = (config.status || 'ONLINE').toUpperCase(); + const portalType = config.portal_type || 'harness'; + const categoryLabel = config.world_category + ? config.world_category.replace(/-/g, ' ').toUpperCase() + : portalType.replace(/-/g, ' ').toUpperCase(); + + // Readiness bar for game-worlds + let readinessHTML = ''; + if (config.readiness_steps) { + const steps = Object.values(config.readiness_steps); + readinessHTML = `
`; + steps.forEach(step => { + readinessHTML += `
`; + }); + readinessHTML += '
'; + } + + // Action label + const actionLabel = config.destination?.action_label + || (config.status === 'online' ? 'ENTER' : config.status === 'downloaded' ? 'LAUNCH' : 'VIEW'); + card.innerHTML = `
-
${config.name}
-
${config.status || 'ONLINE'}
+
+ ${config.name} + ${categoryLabel} +
+
${statusLabel}
${config.description}
+ ${readinessHTML} `; - + card.addEventListener('click', () => { focusPortal(portal); closePortalAtlas(); }); - + grid.appendChild(card); }); - + + // Show empty state + if (visibleCount === 0) { + const empty = document.createElement('div'); + empty.className = 'atlas-empty'; + empty.textContent = atlasSearchQuery + ? `No worlds match "${atlasSearchQuery}"` + : 'No worlds in this category'; + grid.appendChild(empty); + } + document.getElementById('atlas-online-count').textContent = onlineCount; document.getElementById('atlas-standby-count').textContent = standbyCount; + document.getElementById('atlas-downloaded-count').textContent = downloadedCount; + document.getElementById('atlas-total-count').textContent = portals.length; // Update Bannerlord HUD status const bannerlord = portals.find(p => p.config.id === 'bannerlord'); diff --git a/index.html b/index.html index 52feb99c..2209276e 100644 --- a/index.html +++ b/index.html @@ -113,9 +113,9 @@
-
@@ -214,20 +214,35 @@
🌐 -

PORTAL ATLAS

+

WORLD DIRECTORY

+
+ +
+ + + + + + +
+
- +
diff --git a/style.css b/style.css index 3e2aa626..006f60c8 100644 --- a/style.css +++ b/style.css @@ -410,6 +410,123 @@ canvas#nexus-canvas { font-style: italic; } +/* Atlas Controls */ +.atlas-controls { + padding: 15px 30px; + border-bottom: 1px solid var(--color-border); + display: flex; + flex-direction: column; + gap: 12px; +} + +.atlas-search { + width: 100%; + padding: 10px 15px; + background: rgba(20, 30, 60, 0.6); + border: 1px solid var(--color-border); + color: var(--color-text); + font-family: var(--font-body); + font-size: 13px; + outline: none; + transition: border-color 0.2s; +} + +.atlas-search:focus { + border-color: var(--color-primary); +} + +.atlas-search::placeholder { + color: rgba(160, 184, 208, 0.4); +} + +.atlas-filters { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.atlas-filter-btn { + background: transparent; + border: 1px solid var(--color-border); + color: var(--color-text-muted); + padding: 4px 12px; + font-family: var(--font-display); + font-size: 10px; + cursor: pointer; + transition: all 0.2s; + letter-spacing: 1px; +} + +.atlas-filter-btn:hover { + border-color: var(--color-primary); + color: var(--color-primary); +} + +.atlas-filter-btn.active { + background: rgba(74, 240, 192, 0.15); + border-color: var(--color-primary); + color: var(--color-primary); +} + +/* Enhanced Atlas Cards */ +.status-downloaded { background: rgba(255, 165, 0, 0.2); color: #ffa500; border: 1px solid #ffa500; } + +.status-indicator.downloaded { background: #ffa500; box-shadow: 0 0 5px #ffa500; } + +.atlas-card-category { + font-family: var(--font-display); + font-size: 9px; + padding: 2px 6px; + border-radius: 2px; + text-transform: uppercase; + background: rgba(255, 255, 255, 0.05); + color: var(--color-text-muted); + border: 1px solid rgba(255, 255, 255, 0.08); + margin-left: 6px; +} + +.atlas-card-readiness { + display: flex; + gap: 4px; + margin-top: 10px; + margin-bottom: 5px; +} + +.readiness-step { + flex: 1; + height: 3px; + background: rgba(255, 255, 255, 0.1); + border-radius: 1px; + position: relative; +} + +.readiness-step.done { + background: var(--portal-color, var(--color-primary)); +} + +.readiness-step[title] { + cursor: help; +} + +.atlas-card-action { + font-family: var(--font-display); + font-size: 10px; + color: var(--portal-color, var(--color-primary)); + letter-spacing: 1px; +} + +.atlas-total { + color: var(--color-text-muted); +} + +.atlas-empty { + grid-column: 1 / -1; + text-align: center; + padding: 40px; + color: var(--color-text-muted); + font-style: italic; +} + @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; }