Spaces:
Running
Running
document.addEventListener('DOMContentLoaded', () => { | |
// --- CONFIGURATION --- | |
const config = { | |
backendUrl: 'https://ar-city-explorer-backend.aiagents.workers.dev' | |
}; | |
// --- GLOBAL STATE --- | |
let poisData = []; | |
let arActive = false; | |
let userSettings = { | |
showHistorical: true, | |
showMenus: true, | |
showNavigation: false, | |
voiceResponses: true | |
}; | |
// --- DOM ELEMENT SELECTORS --- | |
const arToggle = document.getElementById('arToggle'); | |
const arViewport = document.getElementById('arViewport'); | |
const normalView = document.getElementById('normalView'); | |
const aiAssistant = document.getElementById('aiAssistant'); | |
const userInput = document.getElementById('userInput'); | |
const sendBtn = document.getElementById('sendBtn'); | |
const objectModal = document.getElementById('objectModal'); | |
const objectTitle = document.getElementById('objectTitle'); | |
const objectDescription = document.getElementById('objectDescription'); | |
const objectImage = document.getElementById('objectImage'); | |
const closeObjectModal = document.getElementById('closeObjectModal'); | |
const settingsBtn = document.getElementById('settingsBtn'); | |
const settingsModal = document.getElementById('settingsModal'); | |
const closeSettingsModal = document.getElementById('closeSettingsModal'); | |
const aerodeckSection = document.getElementById('aerodeckSection'); | |
const aerodeckList = document.getElementById('aerodeckList'); | |
// --- CORE & UI FUNCTIONS --- | |
function toggleARView(showAR) { | |
arActive = showAR; | |
if (arActive) { | |
normalView.classList.add('hidden'); | |
arViewport.classList.remove('hidden'); | |
arToggle.innerHTML = '<i class="fas fa-times mr-1"></i> Exit AR'; | |
if (poisData.length === 0) { | |
fetchPoisAndCreateAREntities(); | |
} | |
} else { | |
arViewport.classList.add('hidden'); | |
normalView.classList.remove('hidden'); | |
arToggle.innerHTML = '<i class="fas fa-vr-cardboard mr-1"></i> AR Mode'; | |
} | |
} | |
function fetchPoisAndCreateAREntities() { | |
fetch(`${config.backendUrl}/api/pois`) | |
.then(response => { if (!response.ok) throw new Error('Network error'); return response.json(); }) | |
.then(pois => { | |
poisData = pois; | |
const scene = document.querySelector('a-scene'); | |
if (!scene) return; | |
pois.forEach(poi => { | |
const entity = document.createElement('a-entity'); | |
entity.setAttribute('gps-new-entity-place', { latitude: poi.latitude, longitude: poi.longitude }); | |
const box = document.createElement('a-box'); | |
box.setAttribute('material', 'color: red; opacity: 0.7;'); | |
box.setAttribute('scale', '10 10 10'); | |
box.setAttribute('position', '0 5 0'); | |
box.setAttribute('data-poi-id', poi.id); | |
entity.appendChild(box); | |
const text = document.createElement('a-text'); | |
text.setAttribute('value', poi.name); | |
text.setAttribute('look-at', '[gps-new-camera]'); | |
text.setAttribute('scale', '50 50 50'); | |
text.setAttribute('position', '0 15 0'); | |
entity.appendChild(text); | |
scene.appendChild(entity); | |
}); | |
}) | |
.catch(error => { | |
console.error('Failed to load POIs:', error); | |
alert('Could not load city data. Check connection and backend URL.'); | |
toggleARView(false); | |
}); | |
} | |
function showObjectInfo(poiId) { | |
objectTitle.textContent = 'Loading...'; | |
objectDescription.textContent = ''; | |
aerodeckSection.classList.add('hidden'); | |
fetch(`${config.backendUrl}/api/pois/${poiId}`) | |
.then(response => { if (!response.ok) throw new Error('POI not found'); return response.json(); }) | |
.then(data => { | |
objectTitle.textContent = data.name; | |
objectDescription.textContent = data.description || "No description available."; | |
objectImage.src = `https://via.placeholder.com/300x200?text=${encodeURIComponent(data.name)}`; | |
if (data.aerodecks && data.aerodecks.length > 0) { | |
aerodeckList.innerHTML = ''; | |
data.aerodecks.forEach(deck => { | |
const itemElement = document.createElement('div'); | |
itemElement.className = 'flex justify-between items-center p-2 bg-gray-100 rounded'; | |
const statusColor = deck.status === 'Operational' ? 'text-green-500' : 'text-orange-500'; | |
itemElement.innerHTML = ` | |
<div> | |
<div class="font-medium">${deck.deck_name}</div> | |
<div class="text-sm text-gray-600">Size: ${deck.size_meters}m | Charging: ${deck.is_charging_available ? 'Yes' : 'No'}</div> | |
</div> | |
<div class="font-bold text-sm ${statusColor}">${deck.status}</div> | |
`; | |
aerodeckList.appendChild(itemElement); | |
}); | |
aerodeckSection.classList.remove('hidden'); | |
} | |
objectModal.classList.remove('hidden'); | |
}) | |
.catch(error => { | |
console.error("Error fetching POI details:", error); | |
alert("Could not load details for this location."); | |
}); | |
} | |
function addUserMessage(message) { | |
const chatContainer = aiAssistant.querySelector('.flex-col'); | |
const msg = `<div class="ai-message user-message"><p>${message}</p></div>`; | |
chatContainer.insertAdjacentHTML('beforeend', msg); | |
aiAssistant.scrollTop = aiAssistant.scrollHeight; | |
} | |
function addAIMessage(message) { | |
const chatContainer = aiAssistant.querySelector('.flex-col'); | |
const msg = `<div class="ai-message assistant-message"><div class="font-bold text-indigo-800 mb-1">AR Guide</div><p>${message}</p></div>`; | |
chatContainer.insertAdjacentHTML('beforeend', msg); | |
aiAssistant.scrollTop = aiAssistant.scrollHeight; | |
} | |
function handleSearch() { | |
const searchTerm = userInput.value.trim(); | |
if (!searchTerm) return; | |
addUserMessage(searchTerm); | |
userInput.value = ''; | |
const searchKeywords = ['search', 'find', 'show', 'where is', 'landmark', 'park', 'beach', 'hollywood', 'map', 'navigate']; | |
const isLocalSearch = searchKeywords.some(keyword => searchTerm.toLowerCase().includes(keyword)); | |
if (isLocalSearch) { | |
const stopWords = ['show', 'me', 'find', 'is', 'a', 'the', 'for', 'where', 'search', 'of', 'at']; | |
const searchTokens = searchTerm.toLowerCase().split(' ').filter(word => !stopWords.includes(word) && word.length > 2); | |
if (searchTokens.length === 0) return addAIMessage("Please be more specific in your local search."); | |
const results = poisData.filter(poi => { | |
const poiText = (poi.name + ' ' + (poi.description || '')).toLowerCase(); | |
return searchTokens.some(token => { | |
if (poiText.includes(token)) return true; | |
if (token.endsWith('s')) return poiText.includes(token.slice(0, -1)); | |
return false; | |
}); | |
}); | |
if (results.length > 0) { | |
let responseMessage = `I found ${results.length} local result(s):<ul>`; | |
results.forEach(poi => { responseMessage += `<li class="mt-2 list-disc list-inside">${poi.name}</li>`; }); | |
responseMessage += "</ul>"; | |
addAIMessage(responseMessage); | |
} else { | |
addAIMessage(`Sorry, I couldn't find any local places matching "${searchTerm}".`); | |
} | |
} else { | |
addAIMessage("AI is thinking..."); | |
fetch(`${config.backendUrl}/api/ask`, { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/json' }, | |
body: JSON.stringify({ prompt: searchTerm }) | |
}) | |
.then(response => { if (!response.ok) throw new Error('Network error'); return response.json(); }) | |
.then(data => { | |
const chatContainer = aiAssistant.querySelector('.flex-col'); | |
const thinkingMessage = Array.from(chatContainer.querySelectorAll('.assistant-message')).pop(); | |
if (thinkingMessage && thinkingMessage.textContent.includes("AI is thinking...")) { | |
thinkingMessage.querySelector('p').innerHTML = data.response.replace(/\n/g, '<br>'); | |
} else { | |
addAIMessage(data.response.replace(/\n/g, '<br>')); | |
} | |
}) | |
.catch(error => { | |
console.error('Error asking AI:', error); | |
const chatContainer = aiAssistant.querySelector('.flex-col'); | |
const thinkingMessage = Array.from(chatContainer.querySelectorAll('.assistant-message')).pop(); | |
if (thinkingMessage && thinkingMessage.textContent.includes("AI is thinking...")) { | |
thinkingMessage.querySelector('p').innerHTML = "Sorry, I had trouble connecting to the AI."; | |
} else { | |
addAIMessage("Sorry, I had trouble connecting to the AI."); | |
} | |
}); | |
} | |
} | |
// --- EVENT LISTENERS --- | |
arToggle.addEventListener('click', () => toggleARView(!arActive)); | |
if (document.querySelector('a-scene')) { | |
document.querySelector('a-scene').addEventListener('click', (event) => { | |
if (event.target.hasAttribute('data-poi-id')) { | |
const poiId = parseInt(event.target.getAttribute('data-poi-id'), 10); | |
showObjectInfo(poiId); | |
} | |
}); | |
} | |
closeObjectModal.addEventListener('click', () => objectModal.classList.add('hidden')); | |
sendBtn.addEventListener('click', handleSearch); | |
userInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') handleSearch(); }); | |
settingsBtn.addEventListener('click', () => { settingsModal.classList.remove('hidden'); }); | |
closeSettingsModal.addEventListener('click', () => { settingsModal.classList.add('hidden'); }); | |
settingsModal.addEventListener('click', (event) => { if (event.target === settingsModal) { settingsModal.classList.add('hidden'); } }); | |
const settingToggles = settingsModal.querySelectorAll('input[data-setting]'); | |
settingToggles.forEach(toggle => { | |
const settingName = toggle.dataset.setting; | |
if (userSettings[settingName] !== undefined) { toggle.checked = userSettings[settingName]; } | |
toggle.addEventListener('change', (event) => { | |
const changedSettingName = event.target.dataset.setting; | |
userSettings[changedSettingName] = event.target.checked; | |
console.log('Settings updated:', userSettings); | |
}); | |
}); | |
// --- INITIALIZATION --- | |
fetchPoisAndCreateAREntities(); | |
// --- FINAL SYSTEM CHECK --- | |
const debugLog = document.getElementById('debug-log'); | |
if (debugLog) { | |
let debugMessage = "--- FINAL SYSTEM CHECK ---\n\n"; | |
let testsPassed = true; | |
// Test 1: Check for the settings button itself | |
if (document.getElementById('settingsBtn')) { | |
debugMessage += "β Test 1: Settings Button HTML (id='settingsBtn') was FOUND.\n"; | |
} else { | |
debugMessage += "β Test 1: Settings Button HTML (id='settingsBtn') was NOT FOUND.\n"; | |
testsPassed = false; | |
} | |
// Test 2: Check for the settings modal window | |
if (document.getElementById('settingsModal')) { | |
debugMessage += "β Test 2: Settings Modal HTML (id='settingsModal') was FOUND.\n"; | |
} else { | |
debugMessage += "β Test 2: Settings Modal HTML (id='settingsModal') was NOT FOUND.\n"; | |
testsPassed = false; | |
} | |
// Display the final results | |
debugLog.textContent = debugMessage; | |
if(testsPassed) { | |
debugLog.style.borderColor = 'green'; | |
debugLog.style.color = 'green'; | |
debugLog.style.backgroundColor = '#DFF2BF'; | |
debugLog.textContent += "\nAll elements found. The button should be working."; | |
} | |
} | |
// --- END OF SYSTEM CHECK --- | |
}); |