// Before the app: which server, and the app's sign-in to it. A server older than LLeMbas 2.0 is // refused here, with the reason Rust gave (src-tauri/src/server.rs, `check`). Signing in is the // server's device login: the approval page opens in the system's browser; this page shows the // code to compare and waits. Where the server offers it (`one_approval`) and this computer is not // linked yet, the same approval links it for the agent: there is no second one. import { $, h, clear } from "./dom.js" import { invoke, listen, why } from "./bridge.js" let code = null // the code being approved, while signing in let failed = "" export function renderSetup(s) { const box = clear($("setup-body")) $("setup-version").textContent = s.version const server = h("section", { "aria-labelledby": "setup-server-h" }, h("h2", { id: "setup-server-h", text: t("Server") })) const address = h("input", { id: "address", name: "address", type: "text", inputmode: "url", autocomplete: "url", placeholder: "https://ai.example", spellcheck: "false", value: s.server || "", }) const hint = h("p", { class: "hint", id: "server-hint", "aria-live": "polite" }) if (!s.checked) hint.textContent = t("Looking for a LLeMbas server there…") else if (s.refused) { hint.textContent = s.refused hint.classList.add("is-error") } else if (s.discovery) hint.textContent = t("%(name)s, LLeMbas %(version)s.", { name: s.discovery.name, version: s.discovery.version }) else hint.textContent = t("LLeMbas Desktop needs a LLeMbas 2.0 server or later.") const connect = h("button", { type: "submit", class: "btn btn--primary", text: t("Connect") }) const form = h( "form", { class: "field", novalidate: true }, h("label", { for: "address", text: t("Address of your LLeMbas server") }), h("div", { class: "row" }, address, connect), hint, ) form.addEventListener("submit", async (e) => { e.preventDefault() connect.disabled = true hint.className = "hint" hint.textContent = t("Looking for a LLeMbas server there…") try { await invoke("set_server", { address: address.value }) } catch (err) { hint.className = "hint is-error" hint.textContent = why(err) } finally { connect.disabled = false } }) server.append(form) if (s.refused && s.server) { server.append(h("div", { class: "row--buttons" }, h("button", { type: "button", class: "btn", text: t("Try again"), onclick: () => invoke("recheck_server") }))) } box.append(server) if (s.discovery && !s.account) { const sign = h("section", { "aria-labelledby": "setup-sign-h" }, h("h2", { id: "setup-sign-h", text: t("Sign in") })) const both = s.discovery.one_approval && !s.linked sign.append(h("p", { class: "muted", text: both ? t("The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does, and the same approval links this computer for the agent.") : t("The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.") })) if (s.signing_in && code) { sign.append( h("p", null, t("Approve this code in the page that opened in your browser:")), h("p", { class: "code", text: code.user_code }), h( "div", { class: "row--buttons" }, h("button", { type: "button", class: "btn", text: t("Open the page again"), onclick: () => invoke("open_external", { url: code.verification_uri_complete }) }), h("button", { type: "button", class: "btn", text: t("Cancel"), onclick: () => invoke("sign_in_cancel") }), ), ) } else { sign.append( h( "div", { class: "row--buttons" }, h("button", { type: "button", class: "btn btn--primary", text: s.signing_in ? t("Waiting for the browser…") : t("Sign in"), disabled: s.signing_in, onclick: () => { failed = "" invoke("sign_in").catch((err) => { failed = why(err) renderSetup(s) }) }, }), ), ) } if (failed) sign.append(h("p", { class: "status is-error", text: failed })) box.append(sign) } } export function initSetup(current) { listen("signin", (e) => { const p = e.payload if (p.state === "code") code = p.code if (p.state === "done") code = null if (p.state === "failed") { code = null failed = p.message } renderSetup(current()) }) }