(kind, payload)
| 943 | } |
| 944 | |
| 945 | async function doAction(kind, payload) { |
| 946 | if (kind === "refresh") { toast("Refreshing…"); await loadState(); return; } |
| 947 | if (kind.indexOf("goto:") === 0) { activeTab = kind.slice(5); render(); window.scrollTo(0, 0); return; } |
| 948 | // Autopilot controls stream their results over SSE, so they must not set the |
| 949 | // blocking "pending" banner that freezes the whole view for a one-shot send. |
| 950 | const isAutopilot = kind.indexOf("autopilot_") === 0; |
| 951 | try { |
| 952 | const r = await fetch("/action" + tokenQuery, { |
| 953 | method: "POST", |
| 954 | headers: { "Content-Type": "application/json" }, |
| 955 | body: JSON.stringify({ kind, payload }), |
| 956 | }); |
| 957 | const res = await r.json(); |
| 958 | if (res.ok) { |
| 959 | if (res.state) { |
| 960 | // Local, synchronous actions (refresh, recheck_env) hand back a |
| 961 | // fresh snapshot inline. Apply it now and clear any spinner instead |
| 962 | // of waiting on an SSE broadcast that can be missed if the provider |
| 963 | // restarts or the connection drops — which would spin forever. |
| 964 | state = res.state; |
| 965 | pending = null; |
| 966 | } else if (!isAutopilot) { |
| 967 | pending = res.label || kind; |
| 968 | } |
| 969 | toast(res.message || "Sent to the agent"); |
| 970 | render(); |
| 971 | } else { |
| 972 | toast(res.error || "Action failed"); |
| 973 | } |
| 974 | } catch (e) { |
| 975 | toast("Action failed"); |
| 976 | } |
| 977 | } |
| 978 | |
| 979 | // event delegation for all action buttons + tabs |
| 980 | document.addEventListener("click", (e) => { |
no test coverage detected