// ── Slash commands ────────────────────────────────────────────────────────── // Built-in commands intercepted before send(). Each command runs locally // (no round-trip to the agent) and shows feedback via toast or local message. const COMMANDS=[ {name:'help', desc:'List available commands', fn:cmdHelp}, {name:'clear', desc:'Clear conversation messages', fn:cmdClear}, {name:'model', desc:'Switch model (e.g. /model gpt-4o)', fn:cmdModel, arg:'model_name'}, {name:'workspace', desc:'Switch workspace by name', fn:cmdWorkspace, arg:'name'}, {name:'new', desc:'Start a new chat session', fn:cmdNew}, {name:'usage', desc:'Toggle token usage display on/off', fn:cmdUsage}, ]; function parseCommand(text){ if(!text.startsWith('/'))return null; const parts=text.slice(1).split(/\s+/); const name=parts[0].toLowerCase(); const args=parts.slice(1).join(' ').trim(); return {name,args}; } function executeCommand(text){ const parsed=parseCommand(text); if(!parsed)return false; const cmd=COMMANDS.find(c=>c.name===parsed.name); if(!cmd)return false; cmd.fn(parsed.args); return true; } function getMatchingCommands(prefix){ const q=prefix.toLowerCase(); return COMMANDS.filter(c=>c.name.startsWith(q)); } // ── Command handlers ──────────────────────────────────────────────────────── function cmdHelp(){ const lines=COMMANDS.map(c=>{ const usage=c.arg?` <${c.arg}>`:''; return ` /${c.name}${usage} — ${c.desc}`; }); const msg={role:'assistant',content:'**Available commands:**\n'+lines.join('\n')}; S.messages.push(msg); renderMessages(); showToast('Type / to see commands'); } function cmdClear(){ if(!S.session)return; S.messages=[];S.toolCalls=[]; clearLiveToolCards(); renderMessages(); $('emptyState').style.display=''; showToast('Conversation cleared'); } async function cmdModel(args){ if(!args){showToast('Usage: /model ');return;} const sel=$('modelSelect'); if(!sel)return; const q=args.toLowerCase(); // Fuzzy match: find first option whose label or value contains the query let match=null; for(const opt of sel.options){ if(opt.value.toLowerCase().includes(q)||opt.textContent.toLowerCase().includes(q)){ match=opt.value;break; } } if(!match){showToast(`No model matching "${args}"`);return;} sel.value=match; await sel.onchange(); showToast(`Switched to ${match}`); } async function cmdWorkspace(args){ if(!args){showToast('Usage: /workspace ');return;} try{ const data=await api('/api/workspaces'); const q=args.toLowerCase(); const ws=(data.workspaces||[]).find(w=> (w.name||'').toLowerCase().includes(q)||w.path.toLowerCase().includes(q) ); if(!ws){showToast(`No workspace matching "${args}"`);return;} if(!S.session)return; await api('/api/session/update',{method:'POST',body:JSON.stringify({ session_id:S.session.session_id,workspace:ws.path,model:S.session.model })}); S.session.workspace=ws.path; syncTopbar();await loadDir('.'); showToast(`Switched to workspace: ${ws.name||ws.path}`); }catch(e){showToast('Workspace switch failed: '+e.message);} } async function cmdNew(){ await newSession(); await renderSessionList(); $('msg').focus(); showToast('New session created'); } async function cmdUsage(){ const next=!window._showTokenUsage; window._showTokenUsage=next; try{ await api('/api/settings',{method:'POST',body:JSON.stringify({show_token_usage:next})}); }catch(e){} // Update the settings checkbox if the panel is open const cb=$('settingsShowTokenUsage'); if(cb) cb.checked=next; renderMessages(); showToast('Token usage '+(next?'on':'off')); } // ── Autocomplete dropdown ─────────────────────────────────────────────────── let _cmdSelectedIdx=-1; function showCmdDropdown(matches){ const dd=$('cmdDropdown'); if(!dd)return; dd.innerHTML=''; _cmdSelectedIdx=-1; for(let i=0;i${esc(c.arg)}`:''; el.innerHTML=`
/${esc(c.name)}${usage}
${esc(c.desc)}
`; el.onmousedown=(e)=>{ e.preventDefault(); $('msg').value='/'+c.name+(c.arg?' ':''); hideCmdDropdown(); $('msg').focus(); }; dd.appendChild(el); } dd.classList.add('open'); } function hideCmdDropdown(){ const dd=$('cmdDropdown'); if(dd)dd.classList.remove('open'); _cmdSelectedIdx=-1; } function navigateCmdDropdown(dir){ const dd=$('cmdDropdown'); if(!dd)return; const items=dd.querySelectorAll('.cmd-item'); if(!items.length)return; items.forEach(el=>el.classList.remove('selected')); _cmdSelectedIdx+=dir; if(_cmdSelectedIdx<0)_cmdSelectedIdx=items.length-1; if(_cmdSelectedIdx>=items.length)_cmdSelectedIdx=0; items[_cmdSelectedIdx].classList.add('selected'); } function selectCmdDropdownItem(){ const dd=$('cmdDropdown'); if(!dd)return; const items=dd.querySelectorAll('.cmd-item'); if(_cmdSelectedIdx>=0&&_cmdSelectedIdx{}}); } else if(items.length===1){ items[0].onmousedown({preventDefault:()=>{}}); } hideCmdDropdown(); }