agent-ui / frontend /sessions.js
lvwerra's picture
lvwerra HF Staff
Replace welcome SVG diagram with real timeline component, hide sidebar on welcome
339deb5
// ============================================
// Session Management
// ============================================
async function fetchSessions() {
try {
const response = await apiFetch('/api/sessions');
if (response.ok) {
return await response.json();
}
} catch (e) {
console.error('Failed to fetch sessions:', e);
}
return { sessions: [], current: null };
}
function showSessionSelector(sessions) {
const selector = document.getElementById('sessionSelector');
const welcome = document.getElementById('welcomeMessage');
const sessionIndicator = document.getElementById('sessionIndicator');
const inputArea = document.getElementById('commandInputArea');
// Show welcome message and session selector, hide sidebar
if (welcome) welcome.style.display = 'block';
if (selector) selector.style.display = 'block';
if (sessionIndicator) sessionIndicator.style.display = 'none';
if (inputArea) inputArea.style.display = 'none';
const sidebar = document.getElementById('agentsSidebar');
if (sidebar) sidebar.style.display = 'none';
// Populate existing sessions dropdown
const select = document.getElementById('existingSessionSelect');
if (select) {
select.innerHTML = '<option value="">-- Select session --</option>';
sessions.forEach(session => {
const option = document.createElement('option');
option.value = session.name;
option.textContent = `${session.name} (${formatDate(session.modified)})`;
select.appendChild(option);
});
}
}
function hideSessionSelector() {
const selector = document.getElementById('sessionSelector');
const welcome = document.getElementById('welcomeMessage');
const sessionIndicator = document.getElementById('sessionIndicator');
const inputArea = document.getElementById('commandInputArea');
// Hide session selector, show session indicator, input, and sidebar
if (selector) selector.style.display = 'none';
if (welcome) welcome.style.display = 'block';
if (sessionIndicator) sessionIndicator.style.display = 'block';
if (inputArea) inputArea.style.display = 'block';
const sidebar = document.getElementById('agentsSidebar');
if (sidebar) sidebar.style.display = '';
}
async function onSessionSelected(sessionName) {
currentSession = sessionName;
hideSessionSelector();
// Update session name display
const nameEl = document.getElementById('currentSessionName');
if (nameEl) nameEl.textContent = sessionName;
const renameInput = document.getElementById('currentSessionRename');
if (renameInput) renameInput.value = sessionName;
// Update timeline title to show session name
if (timelineData[0]) {
timelineData[0].title = sessionName;
renderTimeline();
}
// Load workspace for this session
await loadWorkspace();
// Refresh sessions panel list
await refreshSessionsList();
}
async function createSession(name) {
try {
const response = await apiFetch('/api/sessions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name })
});
if (response.ok) {
const data = await response.json();
await onSessionSelected(data.name);
return true;
} else {
const error = await response.json();
alert(error.detail || 'Failed to create session');
}
} catch (e) {
console.error('Failed to create session:', e);
alert('Failed to create session');
}
return false;
}
async function selectSession(name) {
try {
const response = await apiFetch('/api/sessions/select', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name })
});
if (response.ok) {
// Reset local state and load the selected session
resetLocalState();
await onSessionSelected(name);
return true;
} else {
const error = await response.json();
alert(error.detail || 'Failed to select session');
}
} catch (e) {
console.error('Failed to select session:', e);
alert('Failed to select session');
}
return false;
}
async function renameSession(oldName, newName) {
try {
const response = await apiFetch('/api/sessions/rename', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ oldName, newName })
});
if (response.ok) {
const data = await response.json();
currentSession = data.name;
// Update displays
const nameEl = document.getElementById('currentSessionName');
if (nameEl) nameEl.textContent = data.name;
// Update timeline title
if (timelineData[0]) {
timelineData[0].title = data.name;
renderTimeline();
}
await refreshSessionsList();
return true;
} else {
const error = await response.json();
alert(error.detail || 'Failed to rename session');
}
} catch (e) {
console.error('Failed to rename session:', e);
alert('Failed to rename session');
}
return false;
}
function openSessionsPanel() {
const sessionsBtn = document.getElementById('sessionsBtn');
if (sessionsBtn) {
sessionsBtn.click();
}
}
async function refreshSessionsList() {
const sessionsData = await fetchSessions();
const listEl = document.getElementById('sessionsList');
// Update the current session rename input
const renameInput = document.getElementById('currentSessionRename');
if (renameInput && currentSession) {
renameInput.value = currentSession;
}
if (!listEl) return;
if (sessionsData.sessions.length === 0) {
listEl.innerHTML = '<div class="sessions-loading">No other sessions</div>';
return;
}
listEl.innerHTML = '';
sessionsData.sessions.forEach(session => {
const item = document.createElement('div');
item.className = 'sessions-list-item' + (session.name === currentSession ? ' current' : '');
const isCurrent = session.name === currentSession;
item.innerHTML = `
<span class="sessions-list-item-name">${escapeHtml(session.name)}</span>
<span class="sessions-list-item-date">${formatDate(session.modified)}</span>
${!isCurrent ? `<button class="sessions-delete-btn" title="Delete session">×</button>` : ''}
`;
if (!isCurrent) {
// Click on name/date to select
item.querySelector('.sessions-list-item-name').addEventListener('click', () => selectSession(session.name));
item.querySelector('.sessions-list-item-date').addEventListener('click', () => selectSession(session.name));
// Click on delete button to delete
const deleteBtn = item.querySelector('.sessions-delete-btn');
if (deleteBtn) {
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
deleteSession(session.name);
});
}
}
listEl.appendChild(item);
});
}
async function deleteSession(sessionName) {
if (!confirm(`Delete session "${sessionName}"? This cannot be undone.`)) {
return;
}
try {
const response = await apiFetch(`/api/sessions/${encodeURIComponent(sessionName)}`, {
method: 'DELETE'
});
if (!response.ok) {
try {
const error = await response.json();
alert(error.detail || 'Failed to delete session');
} catch {
alert('Failed to delete session');
}
return;
}
// Refresh the sessions list in the panel
refreshSessionsList();
// Also refresh the welcome page dropdown
const sessionsData = await fetchSessions();
const select = document.getElementById('existingSessionSelect');
if (select) {
select.innerHTML = '<option value="">-- Select session --</option>';
sessionsData.sessions.forEach(session => {
const option = document.createElement('option');
option.value = session.name;
option.textContent = `${session.name} (${formatDate(session.modified)})`;
select.appendChild(option);
});
}
} catch (error) {
console.error('Error deleting session:', error);
alert('Failed to delete session');
}
}
function initializeSessionListeners() {
// Welcome page session selector
const createBtn = document.getElementById('createSessionBtn');
const newNameInput = document.getElementById('newSessionName');
const existingSelect = document.getElementById('existingSessionSelect');
// Pre-populate with a cool random name
if (newNameInput) {
generateSessionName().then(name => newNameInput.value = name);
}
// Regenerate button
const regenerateBtn = document.getElementById('regenerateNameBtn');
if (regenerateBtn && newNameInput) {
regenerateBtn.addEventListener('click', async () => {
newNameInput.value = await generateSessionName();
});
}
if (createBtn) {
createBtn.addEventListener('click', async () => {
const name = newNameInput?.value.trim();
if (name) {
createSession(name);
} else {
// Auto-generate name
createSession(await generateSessionName());
}
});
}
if (newNameInput) {
newNameInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
createBtn?.click();
}
});
}
if (existingSelect) {
existingSelect.addEventListener('change', () => {
const name = existingSelect.value;
if (name) {
selectSession(name);
}
});
}
// Sessions panel handlers are set up at the end of the file with other panels
// Panel rename button
const renameBtn = document.getElementById('renameSessionBtn');
const renameInput = document.getElementById('currentSessionRename');
if (renameBtn && renameInput) {
renameBtn.addEventListener('click', () => {
const newName = renameInput.value.trim();
if (newName && newName !== currentSession) {
renameSession(currentSession, newName);
}
});
}
// Panel create new session
const panelCreateBtn = document.getElementById('panelCreateSessionBtn');
const panelNewNameInput = document.getElementById('panelNewSessionName');
const panelRegenerateBtn = document.getElementById('panelRegenerateNameBtn');
// Pre-populate panel input with cool name too
if (panelNewNameInput) {
generateSessionName().then(name => panelNewNameInput.value = name);
}
// Panel regenerate button
if (panelRegenerateBtn && panelNewNameInput) {
panelRegenerateBtn.addEventListener('click', async () => {
panelNewNameInput.value = await generateSessionName();
});
}
if (panelCreateBtn) {
panelCreateBtn.addEventListener('click', async () => {
const name = panelNewNameInput?.value.trim();
if (name) {
resetLocalState();
await createSession(name);
// Pre-populate a new name for next time
if (panelNewNameInput) {
panelNewNameInput.value = await generateSessionName();
}
}
});
}
}