diff --git a/web/app.js b/web/app.js index 82fd420..72e04ef 100644 --- a/web/app.js +++ b/web/app.js @@ -50,9 +50,11 @@ const updateModalClose = document.getElementById("update-modal-close"); const updateModalInstall = document.getElementById("update-modal-install"); const updateModalReleases = document.getElementById("update-modal-releases"); const plansRefreshButton = document.getElementById("plans-refresh"); +const plansCloseButton = document.getElementById("plans-close"); const planForm = document.getElementById("plan-form"); const plansStatusEl = document.getElementById("plans-status"); const plansDashboardEl = document.getElementById("plans-dashboard"); +const plansRailListEl = document.getElementById("plans-rail-list"); let ollamaOnline = true; let latestUpdate = null; @@ -742,7 +744,6 @@ function toggleSidebarPanel(panelName) { const panels = { settings: { panel: settingsPanel, button: settingsToggle }, memory: { panel: memoryPanel, button: memoryToggle }, - plans: { panel: plansPanel, button: plansToggle }, ollama: { panel: ollamaPanel, button: ollamaToggle }, }; const target = panels[panelName]; @@ -763,7 +764,6 @@ function toggleSidebarPanel(panelName) { checkForUpdate(); } if (panelName === "memory") refreshMemory(); - if (panelName === "plans") refreshPlans(); if (panelName === "ollama") { refreshConfig(); refreshOllamaStatus(); @@ -971,6 +971,19 @@ function closeNegotiationPanel() { negotiationStatusEl.textContent = ""; } +function openPlansPanel(openPlanId = null) { + if (!plansPanel) return; + plansPanel.hidden = false; + plansToggle?.setAttribute("aria-expanded", "true"); + refreshPlans(openPlanId); +} + +function closePlansPanel() { + if (!plansPanel) return; + plansPanel.hidden = true; + plansToggle?.setAttribute("aria-expanded", "false"); +} + function renderNegotiationMessages(data) { negotiationMessagesEl.innerHTML = ""; const items = Array.isArray(data) ? data : [data].filter(Boolean); @@ -1062,13 +1075,47 @@ async function createPlan(event) { } async function refreshPlans(openPlanId = null) { - if (!plansDashboardEl) return; + if (!plansDashboardEl && !plansRailListEl) return; try { const response = await fetch("/api/plans"); const result = await response.json(); - await renderPlans(result.plans || [], openPlanId); + const plans = result.plans || []; + renderPlansRail(plans); + if (plansDashboardEl) await renderPlans(plans, openPlanId); } catch (error) { - plansDashboardEl.textContent = `Plans failed: ${fetchErrorMessage(error)}`; + if (plansDashboardEl) plansDashboardEl.textContent = `Plans failed: ${fetchErrorMessage(error)}`; + if (plansRailListEl) plansRailListEl.textContent = `Plans failed: ${fetchErrorMessage(error)}`; + } +} + +function renderPlansRail(plans) { + if (!plansRailListEl) return; + plansRailListEl.innerHTML = ""; + if (!plans.length) { + plansRailListEl.innerHTML = '
No plans
'; + return; + } + for (const plan of plans.slice(0, 5)) { + const row = document.createElement("button"); + row.type = "button"; + row.className = "plan-rail-item"; + const title = document.createElement("span"); + title.className = "plan-rail-title"; + title.textContent = plan.title || "Untitled plan"; + const status = document.createElement("span"); + status.className = "plan-rail-status"; + status.textContent = plan.status || "plan"; + row.append(title, status); + row.addEventListener("click", () => openPlansPanel(plan.id)); + plansRailListEl.appendChild(row); + } + if (plans.length > 5) { + const more = document.createElement("button"); + more.type = "button"; + more.className = "plan-rail-item"; + more.textContent = `${plans.length - 5} more`; + more.addEventListener("click", () => openPlansPanel()); + plansRailListEl.appendChild(more); } } @@ -1385,9 +1432,13 @@ configRefreshButton?.addEventListener("click", refreshConfig); configForm?.addEventListener("submit", saveConfig); settingsToggle?.addEventListener("click", () => toggleSidebarPanel("settings")); memoryToggle?.addEventListener("click", () => toggleSidebarPanel("memory")); -plansToggle?.addEventListener("click", () => toggleSidebarPanel("plans")); +plansToggle?.addEventListener("click", () => { + if (plansPanel?.hidden) openPlansPanel(); + else closePlansPanel(); +}); ollamaToggle?.addEventListener("click", () => toggleSidebarPanel("ollama")); plansRefreshButton?.addEventListener("click", () => refreshPlans()); +plansCloseButton?.addEventListener("click", closePlansPanel); planForm?.addEventListener("submit", createPlan); ollamaForm?.addEventListener("submit", saveOllamaConfig); ollamaRefreshButton?.addEventListener("click", refreshOllamaStatus); diff --git a/web/index.html b/web/index.html index 5ced0b9..4c6e48a 100644 --- a/web/index.html +++ b/web/index.html @@ -9,7 +9,7 @@
-