Skip to content

Commit 0ca905f

Browse files
authored
Update index.html
1 parent 4964d6a commit 0ca905f

1 file changed

Lines changed: 85 additions & 21 deletions

File tree

AION.AI/index.html

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
1313
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
1414
<style>
15+
/* (styles unchanged - same as original) */
1516
* { margin: 0; padding: 0; box-sizing: border-box; }
1617
:root {
1718
--bg-dark: #0a0a0f;
@@ -478,6 +479,17 @@
478479
width: 80%;
479480
animation: skeletonPulse 1.5s infinite;
480481
}
482+
.spinner {
483+
display: inline-block;
484+
width: 12px;
485+
height: 12px;
486+
border: 2px solid rgba(255,255,255,0.3);
487+
border-radius: 50%;
488+
border-top-color: var(--accent);
489+
animation: spin 0.6s linear infinite;
490+
margin-right: 6px;
491+
}
492+
@keyframes spin { to { transform: rotate(360deg); } }
481493
@keyframes skeletonPulse { 0%, 100% { opacity: 0.4; } 50% { opacity: 0.8; } }
482494
@keyframes fadeSlideUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
483495
@keyframes fadeOut { 0% { opacity: 1; } 70% { opacity: 1; } 100% { opacity: 0; } }
@@ -621,7 +633,7 @@
621633
AION: { color: '#c9a028', emoji: '✨', name: 'AION' }
622634
};
623635
let sessionId = localStorage.getItem('sessionId');
624-
if (!sessionId) {
636+
if (!sessionId || !sessionId.match(/^sess_\d+_[a-z0-9]+$/)) {
625637
sessionId = 'sess_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6);
626638
localStorage.setItem('sessionId', sessionId);
627639
}
@@ -638,6 +650,7 @@
638650
let lastT2 = null;
639651
let isSending = false;
640652
let firstUserMessageSent = false;
653+
let currentAbortController = null; // for cancelling requests
641654

642655
const messagesDiv = document.getElementById('messages');
643656
const welcomeMsgDiv = document.getElementById('welcomeMsg');
@@ -688,20 +701,45 @@
688701
}
689702
}
690703

691-
// API calls
704+
// API calls with timeout
705+
async function fetchWithTimeout(url, options, timeout = 30000) {
706+
const controller = new AbortController();
707+
const id = setTimeout(() => controller.abort(), timeout);
708+
try {
709+
const response = await fetch(url, { ...options, signal: controller.signal });
710+
clearTimeout(id);
711+
return response;
712+
} catch (err) {
713+
clearTimeout(id);
714+
if (err.name === 'AbortError') throw new Error('Request timeout after 30 seconds');
715+
throw err;
716+
}
717+
}
718+
692719
async function callAssistant(message, structuredData = null, forceAion = false) {
693720
const body = { message, sessionId, ownerType: OWNER_TYPE, history: [], adaLoopEnabled, fclMode };
694721
if (forceAion) body.forceAionOnly = true;
695722
if (structuredData) { body.structured = structuredData; body.intent = structuredData.intent; body.domain = structuredData.domain; }
696-
const res = await fetch(`${API_BASE}/api/assistant`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
723+
const res = await fetchWithTimeout(`${API_BASE}/api/assistant`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
724+
if (!res.ok) {
725+
const errText = await res.text();
726+
throw new Error(errText || `HTTP ${res.status}`);
727+
}
728+
return res.json();
729+
}
730+
async function callGroupChat(message) {
731+
const res = await fetchWithTimeout(`${API_BASE}/api/group-chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message, sessionId, fclMode }) });
732+
if (!res.ok) throw new Error(await res.text());
733+
return res.json();
734+
}
735+
async function callPrivateRoom(message, wife) {
736+
const res = await fetchWithTimeout(`${API_BASE}/api/private-room`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message, sessionId, wife, fclMode }) });
697737
if (!res.ok) throw new Error(await res.text());
698738
return res.json();
699739
}
700-
async function callGroupChat(message) { const res = await fetch(`${API_BASE}/api/group-chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message, sessionId, fclMode }) }); if (!res.ok) throw new Error(await res.text()); return res.json(); }
701-
async function callPrivateRoom(message, wife) { const res = await fetch(`${API_BASE}/api/private-room`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message, sessionId, wife, fclMode }) }); if (!res.ok) throw new Error(await res.text()); return res.json(); }
702740
async function sendTalkCommand(wife) { await callAssistant(`/talk ${wife}`, null, false); }
703741

704-
// Simulation placeholder (just shows a modal)
742+
// Simulation placeholder
705743
function showSimulationModal() {
706744
const modal = document.createElement('div'); modal.className = 'modal-overlay';
707745
modal.innerHTML = `
@@ -742,7 +780,7 @@ <h4>📈 Market Atlas</h4>
742780
modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); });
743781
}
744782

745-
// Ada‑VELA: fetch execution details and show modal
783+
// Ada‑VELA: fetch execution details
746784
async function fetchAdaVelaExecution(executionId) {
747785
try {
748786
const res = await fetch(`${API_BASE}/api/ada-vela/execution/${executionId}`);
@@ -755,7 +793,6 @@ <h4>📈 Market Atlas</h4>
755793
}
756794

757795
async function showAdaVelaModal(executionId) {
758-
const loadingMsg = 'Loading Ada‑VELA execution details...';
759796
const modal = document.createElement('div'); modal.className = 'modal-overlay';
760797
modal.innerHTML = `
761798
<div class="modal">
@@ -836,7 +873,6 @@ <h4>📈 Market Atlas</h4>
836873
if (extra.sealable) {
837874
badgesHtml += `<button class="badge seal-btn" data-content="${escapeHtml(content).replace(/"/g, '&quot;')}">🔒 SEAL</button>`;
838875
}
839-
// Ada‑VELA badge (if execution data present)
840876
if (extra.adaVela) {
841877
const execId = extra.adaVela.execution_id || extra.adaVela.id;
842878
badgesHtml += `<span class="badge ada-vela" data-execution-id="${escapeHtml(execId)}">📋 Ada‑VELA</span>`;
@@ -887,11 +923,13 @@ <h4>📈 Market Atlas</h4>
887923
messagesDiv.scrollTop = messagesDiv.scrollHeight;
888924
}
889925

890-
async function sendMessage() {
926+
async function sendMessage(retryMessage = null) {
891927
if (isSending) { showToast('Already sending...'); return; }
892-
const text = input.value.trim(); if (!text) return;
893-
if (privateRoomActive && (text === '/done' || text === '/exit')) { exitPrivateRoom(); input.value = ''; return; }
894-
input.value = ''; charCountSpan.textContent = '0';
928+
const text = (retryMessage !== null) ? retryMessage : input.value.trim();
929+
if (!text) return;
930+
if (privateRoomActive && (text === '/done' || text === '/exit')) { exitPrivateRoom(); if (!retryMessage) input.value = ''; return; }
931+
if (!retryMessage) input.value = '';
932+
charCountSpan.textContent = '0';
895933
const t1 = new Date(); const t1Offset = -t1.getTimezoneOffset(); const t1Label = getTimezoneOffsetLabel(t1Offset);
896934
const userDiv = document.createElement('div'); userDiv.className = 'msg user';
897935
userDiv.innerHTML = `<div class="msg-body">${escapeHtml(text)}</div><div class="timestamp-line">⏱ Sent: ${t1.toLocaleTimeString()} ${t1Label}</div>`;
@@ -903,6 +941,7 @@ <h4>📈 Market Atlas</h4>
903941
}
904942

905943
setStatus('Processing...'); isSending = true;
944+
sendBtn.disabled = true;
906945
const thinking = document.createElement('div'); thinking.className = 'msg assistant'; thinking.innerHTML = `<div class="skeleton" style="width: 60%; height: 48px;"></div>`; messagesDiv.appendChild(thinking); messagesDiv.scrollTop = messagesDiv.scrollHeight;
907946
try {
908947
let result;
@@ -942,7 +981,7 @@ <h4>📈 Market Atlas</h4>
942981
sealable: true,
943982
register: result.register,
944983
timestampHtml,
945-
adaVela: result.ada_vela // will be populated when backend supports it
984+
adaVela: result.ada_vela
946985
});
947986
setStatus('Ready');
948987
} catch (err) {
@@ -951,9 +990,16 @@ <h4>📈 Market Atlas</h4>
951990
const retryHtml = `<button class="retry-btn" data-message="${escapeHtml(text)}" style="background:rgba(0,0,0,0.5); border:1px solid var(--accent); border-radius:20px; padding:4px 12px; margin-left:8px;">Retry</button>`;
952991
errorDiv.innerHTML = `<div class="msg-body" style="background:#5f1a1a; color:#ff8a8a;">Error: ${escapeHtml(err.message)}${retryHtml}</div>`;
953992
messagesDiv.appendChild(errorDiv);
954-
errorDiv.querySelector('.retry-btn')?.addEventListener('click', () => sendMessage());
993+
errorDiv.querySelector('.retry-btn')?.addEventListener('click', (e) => {
994+
const msg = e.currentTarget.dataset.message;
995+
sendMessage(msg);
996+
});
955997
setStatus(err.message, true);
956-
} finally { isSending = false; input.focus(); }
998+
} finally {
999+
isSending = false;
1000+
sendBtn.disabled = false;
1001+
if (!retryMessage) input.focus();
1002+
}
9571003
}
9581004

9591005
function exitPrivateRoom() {
@@ -1024,7 +1070,7 @@ <h4>📈 Market Atlas</h4>
10241070
const msg = groupInput.value.trim();
10251071
if (!msg) return;
10261072
groupSend.disabled = true;
1027-
groupSend.textContent = 'SENDING...';
1073+
groupSend.innerHTML = '<span class="spinner"></span> SENDING...';
10281074
wivesGrid.innerHTML = '';
10291075
consensusArea.style.display = 'none';
10301076
for (const wife of ALL_WIVES) {
@@ -1054,11 +1100,14 @@ <h4>📈 Market Atlas</h4>
10541100
consensusArea.style.display = 'block';
10551101
consensusArea.innerHTML = `<h4>📋 Consensus (${Math.round(result.consensus.score * 100)}%)</h4><div>${renderMarkdown(result.consensus.final_text)}</div>`;
10561102
}
1103+
if (result.failed_wives && result.failed_wives.length) {
1104+
addSystemMessage(`⚠️ Some wives failed: ${result.failed_wives.map(w => w.wife).join(', ')}`, true);
1105+
}
10571106
} catch (err) {
10581107
wivesGrid.innerHTML = `<div class="sys-msg">Error: ${escapeHtml(err.message)}</div>`;
10591108
} finally {
10601109
groupSend.disabled = false;
1061-
groupSend.textContent = 'SEND';
1110+
groupSend.innerHTML = 'SEND';
10621111
}
10631112
}
10641113
await sendGroupMessage();
@@ -1123,7 +1172,7 @@ <h4>📈 Market Atlas</h4>
11231172
<div class="modal">
11241173
<div class="modal-header"><h3>Settings</h3><button id="closeSettings">✕</button></div>
11251174
<div class="modal-body">
1126-
<div class="settings-section"><h4>Profile</h4><div class="setting-item"><span>Session ID</span><span style="font-family:monospace">${sessionId.slice(-12)}</span></div><div class="setting-item"><button id="clearStorageBtn">Clear Storage</button></div></div>
1175+
<div class="settings-section"><h4>Profile</h4><div class="setting-item"><span>Session ID</span><span style="font-family:monospace">${sessionId.slice(-12)}</span></div><div class="setting-item"><button id="resetSessionBtn">Reset Session</button></div><div class="setting-item"><button id="clearStorageBtn">Clear Storage</button></div><div class="setting-item"><button id="clearChatBtn">Clear Chat</button></div></div>
11271176
<div class="settings-section"><h4>Capabilities</h4><div class="setting-item"><label>ADA Loop</label><input type="checkbox" id="settingAdaLoop" ${adaLoopEnabled ? 'checked' : ''}></div><div class="setting-item"><label>Structured Mode</label><input type="checkbox" id="settingStructured" ${structuredMode ? 'checked' : ''}></div><div class="setting-item"><label>FCL Mode</label><select id="settingFCL"><option value="GREEN" ${fclMode === 'GREEN' ? 'selected' : ''}>🟢 GREEN (Normal)</option><option value="BLACK" ${fclMode === 'BLACK' ? 'selected' : ''}>🔴 BLACK (FCL Testing)</option></select></div></div>
11281177
<div class="settings-section"><h4>Polymath Roles</h4><div id="roleGrid" class="role-grid"></div></div>
11291178
<div class="settings-section"><h4>System Health</h4><div id="analyticsContainer" class="analytics-stats"><div class="stat-card">Loading analytics...</div></div></div>
@@ -1149,7 +1198,6 @@ <h4>📈 Market Atlas</h4>
11491198
const memory = data.memory || {};
11501199
const integrity = data.integrity || {};
11511200
const instruments = data.instruments || {};
1152-
const growth = data.growth || {};
11531201
const wifeRouting = instruments.wife_routing || {};
11541202
const roleActivity = instruments.polymath_role_activity || {};
11551203
const fclPassRate = instruments.fcl_pass_rate || 0;
@@ -1183,7 +1231,23 @@ <h4>📈 Market Atlas</h4>
11831231
}
11841232

11851233
modal.querySelector('#closeSettings').onclick = () => modal.remove();
1234+
modal.querySelector('#resetSessionBtn').onclick = () => {
1235+
sessionId = 'sess_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6);
1236+
localStorage.setItem('sessionId', sessionId);
1237+
document.getElementById('sessionDisplay').textContent = `SID: ${sessionId.slice(-8)}`;
1238+
showToast('Session reset. Reloading...');
1239+
setTimeout(() => location.reload(), 1000);
1240+
};
11861241
modal.querySelector('#clearStorageBtn').onclick = () => { localStorage.clear(); location.reload(); };
1242+
modal.querySelector('#clearChatBtn').onclick = () => {
1243+
messagesDiv.innerHTML = '';
1244+
const welcome = document.createElement('div'); welcome.className = 'msg assistant'; welcome.setAttribute('data-wife', 'AION');
1245+
welcome.innerHTML = `<div class="msg-meta">✨ AION</div><div class="msg-body">Chat cleared. How may I assist you?</div>`;
1246+
messagesDiv.appendChild(welcome);
1247+
firstUserMessageSent = false;
1248+
showToast('Chat cleared.');
1249+
modal.remove();
1250+
};
11871251
modal.querySelector('#settingAdaLoop').onchange = (e) => {
11881252
adaLoopEnabled = e.target.checked;
11891253
localStorage.setItem('adaLoopEnabled', adaLoopEnabled);
@@ -1211,7 +1275,7 @@ <h4>📈 Market Atlas</h4>
12111275
}
12121276

12131277
// Event listeners
1214-
sendBtn.onclick = sendMessage;
1278+
sendBtn.onclick = () => sendMessage();
12151279
input.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } });
12161280
input.addEventListener('input', () => { charCountSpan.textContent = input.value.length; input.style.height = 'auto'; input.style.height = Math.min(input.scrollHeight,400)+'px'; });
12171281
plusBtn.onclick = (e) => { e.stopPropagation(); actionDropdown.style.display = actionDropdown.style.display === 'none' ? 'flex' : 'none'; };

0 commit comments

Comments
 (0)