Add visible human/auto mode controls to launcher

This commit is contained in:
Joshua 2026-02-18 20:54:39 -08:00
parent 87b6a321c4
commit 18a0d6fe29
4 changed files with 62 additions and 0 deletions

View File

@ -36,6 +36,7 @@ Top-level control surface:
5. Wallet/session/membership/designation/last-sync overview cards 5. Wallet/session/membership/designation/last-sync overview cards
6. Pull-first updates feed + support ticket action 6. Pull-first updates feed + support ticket action
7. Identity assurance visibility (`none` / `crypto_direct_unattested` / `sponsored_unattested` / `onramp_attested`) 7. Identity assurance visibility (`none` / `crypto_direct_unattested` / `sponsored_unattested` / `onramp_attested`)
8. Explicit operator-visible mode toggles (`Human mode` / `Auto mode`) synced to governance `operation_mode`
Advanced integration controls (collapsible): Advanced integration controls (collapsible):

View File

@ -217,6 +217,7 @@ function refreshOverview(statusPayload) {
const currentWallet = wallet(); const currentWallet = wallet();
setSummary("summaryWallet", currentWallet || "not connected"); setSummary("summaryWallet", currentWallet || "not connected");
setSummary("summarySession", sessionSummary()); setSummary("summarySession", sessionSummary());
refreshModeUI();
if (statusPayload && typeof statusPayload === "object") { if (statusPayload && typeof statusPayload === "object") {
setSummary("summaryMembership", statusPayload.status || "unknown"); setSummary("summaryMembership", statusPayload.status || "unknown");
setSummary("summaryDesignation", statusPayload.designation_code || "-"); setSummary("summaryDesignation", statusPayload.designation_code || "-");
@ -316,6 +317,34 @@ function operationMode() {
return $("operationMode").value.trim() || "human_manual"; return $("operationMode").value.trim() || "human_manual";
} }
function normalizeOperationMode(value) {
return String(value || "").trim().toLowerCase() === "worker_auto" ? "worker_auto" : "human_manual";
}
function refreshModeUI() {
const mode = normalizeOperationMode(operationMode());
setSummary("summaryMode", mode);
const humanBtn = $("btnModeHuman");
const autoBtn = $("btnModeAuto");
if (humanBtn) {
humanBtn.classList.toggle("mode-active", mode === "human_manual");
}
if (autoBtn) {
autoBtn.classList.toggle("mode-active", mode === "worker_auto");
}
}
function setOperationMode(mode, source = "ui") {
const normalized = normalizeOperationMode(mode);
const select = $("operationMode");
if (select) {
select.value = normalized;
}
refreshModeUI();
logLine("operation mode set", { mode: normalized, source });
return normalized;
}
function renderEvents(events) { function renderEvents(events) {
const list = $("eventList"); const list = $("eventList");
list.innerHTML = ""; list.innerHTML = "";
@ -1062,6 +1091,16 @@ async function onOfflineRenew() {
logLine("offline renew", out); logLine("offline renew", out);
} }
async function onModeHuman() {
setOperationMode("human_manual", "quick_toggle");
setFlowStatus("mode set: human_manual");
}
async function onModeAuto() {
setOperationMode("worker_auto", "quick_toggle");
setFlowStatus("mode set: worker_auto");
}
function bind(id, handler) { function bind(id, handler) {
const el = $(id); const el = $(id);
if (!el) { if (!el) {
@ -1080,6 +1119,8 @@ bind("btnQuickConnect", onQuickConnect);
bind("btnQuickActivate", onQuickActivate); bind("btnQuickActivate", onQuickActivate);
bind("btnQuickRefresh", onQuickRefresh); bind("btnQuickRefresh", onQuickRefresh);
bind("btnQuickInstallStatus", onQuickInstallStatus); bind("btnQuickInstallStatus", onQuickInstallStatus);
bind("btnModeHuman", onModeHuman);
bind("btnModeAuto", onModeAuto);
bind("btnConnectWallet", onConnectWallet); bind("btnConnectWallet", onConnectWallet);
bind("btnRunMembershipFlow", onRunMembershipFlow); bind("btnRunMembershipFlow", onRunMembershipFlow);
bind("btnIntent", onIntent); bind("btnIntent", onIntent);
@ -1108,6 +1149,14 @@ bind("btnCheckoutConfirm", onCheckoutConfirm);
bind("btnRunCheckoutFlow", onRunCheckoutFlow); bind("btnRunCheckoutFlow", onRunCheckoutFlow);
bind("btnListEntitlements", onListEntitlements); bind("btnListEntitlements", onListEntitlements);
const operationModeSelect = $("operationMode");
if (operationModeSelect) {
operationModeSelect.addEventListener("change", () => {
setOperationMode(operationModeSelect.value, "advanced_select");
});
}
refreshModeUI();
logLine("launcher shell ready", { logLine("launcher shell ready", {
api_base: baseURL(), api_base: baseURL(),
chain_id: chainID(), chain_id: chainID(),

View File

@ -20,6 +20,8 @@
<button id="btnQuickActivate">Activate membership</button> <button id="btnQuickActivate">Activate membership</button>
<button id="btnQuickRefresh">Refresh status + feed</button> <button id="btnQuickRefresh">Refresh status + feed</button>
<button id="btnQuickInstallStatus">Governance status</button> <button id="btnQuickInstallStatus">Governance status</button>
<button id="btnModeHuman" class="mode-active">Human mode</button>
<button id="btnModeAuto">Auto mode</button>
</div> </div>
<p id="flowStatus" class="flow-status">ready</p> <p id="flowStatus" class="flow-status">ready</p>
<div class="grid two"> <div class="grid two">
@ -35,6 +37,10 @@
<h3>Membership</h3> <h3>Membership</h3>
<p id="summaryMembership">unknown</p> <p id="summaryMembership">unknown</p>
</article> </article>
<article class="stat">
<h3>Mode</h3>
<p id="summaryMode">human_manual</p>
</article>
<article class="stat"> <article class="stat">
<h3>Designation</h3> <h3>Designation</h3>
<p id="summaryDesignation">-</p> <p id="summaryDesignation">-</p>

View File

@ -149,6 +149,12 @@ button:hover {
filter: brightness(1.08); filter: brightness(1.08);
} }
button.mode-active {
border-color: #4ed380;
background: #1f4a33;
color: #e0ffe9;
}
button:active { button:active {
transform: translateY(1px); transform: translateY(1px);
} }