@@ -2377,11 +2383,18 @@
renderBgTasks();
}
const planBar = document.getElementById('planBar');
- let planCollapsed = false; // persists across the agent's repeated update_plan re-renders
+ let planCollapsed = false; // persists across the agent's repeated update_plan re-renders (within a run)
+ let planWasDone = false; // rising-edge latch → auto-collapse a finished plan exactly once
function renderPlan(todos){
- if (!todos || !todos.length){ planBar.style.display = 'none'; planBar.innerHTML = ''; return; }
+ // Empty list = clear (agentStart / newChat / dismiss). Reset the collapse state so the NEXT plan
+ // starts fresh and expanded rather than inheriting a previous run's collapsed/done latches.
+ if (!todos || !todos.length){ planBar.style.display = 'none'; planBar.innerHTML = ''; planCollapsed = false; planWasDone = false; return; }
const total = todos.length, done = todos.filter(t => t && t.status === 'done').length;
const pct = Math.round(done / total * 100), allDone = done === total;
+ // A finished checklist shouldn't keep hogging up to 38vh: auto-collapse it to the (green) header the
+ // moment it first completes. Rising-edge only, so a manual re-expand of a done plan sticks afterward.
+ if (allDone && !planWasDone){ planCollapsed = true; }
+ planWasDone = allDone;
let items = '';
for (const t of todos){
const st = t.status === 'done' ? 'done' : t.status === 'in_progress' ? 'doing' : 'todo';
@@ -2389,12 +2402,21 @@
items += '' + ic + '' + esc(t.title) + '
';
}
const head = '' + codicon('chevron-down') + ''
- + 'Plan' + done + '/' + total + '
'
+ + 'Plan' + done + '/' + total + ''
+ + '×'
+ '';
planBar.innerHTML = ''
+ head + '
' + items + '
';
const phead = planBar.querySelector('.phead');
if (phead){ phead.onclick = () => { planCollapsed = !planCollapsed; const c = planBar.querySelector('.plancard'); if (c){ c.classList.toggle('collapsed', planCollapsed); } }; }
+ // Dismiss (×): close the finished — or abandoned — plan outright. stopPropagation so the header's
+ // collapse-toggle doesn't also fire. Clearing resets the latches via the empty-todos branch above.
+ const pdismiss = planBar.querySelector('.pdismiss');
+ if (pdismiss){
+ const dismiss = (e) => { e.preventDefault(); e.stopPropagation(); renderPlan([]); };
+ pdismiss.onclick = dismiss;
+ pdismiss.onkeydown = (e) => { if (e.key === 'Enter' || e.key === ' '){ dismiss(e); } }; // keyboard-activate, like ctxStat / approvals
+ }
planBar.style.display = 'block';
scrollIfStuck(); // log shrank as the sticky plan appeared — keep pinned if we were at bottom
}
@@ -2731,7 +2753,7 @@
renderRouting();
}
- // Footer routing indicator: st-local = WHERE requests go, st-key = the key/plan.
+ // Footer routing indicator: st-key = the mode + plan (e.g. "Gateway · Pro+", "BYO key", "Direct · no key").
// Driven by the latest 'config' (provider) + 'account' (signedIn, mode, plan).
function setStatusItem(id, label, title){
const el = document.getElementById(id);
@@ -2742,13 +2764,10 @@
function renderRouting(){
const gateway = account.signedIn && account.mode === 'gateway';
if (gateway){
- setStatusItem('st-local', 'LevelCode Cloud', 'Requests run through your metered LevelCode Cloud plan (gateway).');
setStatusItem('st-key', 'Gateway' + (account.plan ? ' · ' + account.plan : ''), 'Metered gateway — no provider key needed.');
} else if (lastProvider === 'ollama'){
- setStatusItem('st-local', 'Local', 'Requests go to a local model on your machine (Ollama) — no key, no backend.');
setStatusItem('st-key', 'Direct · no key', 'Local model — no API key needed.');
} else {
- setStatusItem('st-local', 'Direct', 'Requests go directly from your machine to your provider — no LevelCode backend.');
setStatusItem('st-key', 'BYO key', 'Bring your own API key, stored in your OS keychain.');
}
}