Merge branch 'view-ui'
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
ca0f148b78
29 files changed
+623
-55
No files matched your search
@@ -1,5 +1,5 @@
|
||||
// Jarl web UI: router, layout and login.
|
||||
import { h, mount, toast, errorBox, plural } from "./ui.js";
|
||||
import { h, mount, toast, errorBox, plural, lastList } from "./ui.js";
|
||||
import { wordmark, logo } from "./brand.js";
|
||||
import { api, get, state, loadDraft, onChange, setUnauthorizedHandler, setImmediate, setCodePrompt } from "./api.js";
|
||||
import * as totp from "./totp.js";
|
||||
@@ -50,7 +50,7 @@ const navItems = [
|
||||
];
|
||||
|
||||
const app = document.getElementById("app");
|
||||
let mainEl, navEl, barEl, current = { key: "" };
|
||||
let mainEl, navEl, barEl, current = { key: "" }, routeGen = 0;
|
||||
|
||||
function parseHash() {
|
||||
const raw = location.hash.replace(/^#\/?/, "");
|
||||
@@ -153,8 +153,7 @@ function layout() {
|
||||
const header = h("header", { class: "top" },
|
||||
h("a", { class: "brand", href: "#/", title: "Dashboard" },
|
||||
logo(30),
|
||||
wordmark({ rule: false, height: 19, cls: "brand-name" }),
|
||||
h("span", { class: "brand-version" }, me.version)),
|
||||
wordmark({ rule: false, height: 19, cls: "brand-name" })),
|
||||
navEl,
|
||||
h("div", { class: "spacer" }),
|
||||
modeEl = h("span"),
|
||||
@@ -162,7 +161,7 @@ function layout() {
|
||||
h("a", { href: "#/account", class: "user", title: (me.permissions || []).join("\n") }, me.user.username),
|
||||
me.admin ? h("a", { href: "#/users" }, "Users") : null,
|
||||
h("button", { class: "small", onclick: logout }, "Log out"));
|
||||
mount(app, header, barEl, mainEl);
|
||||
mount(app, h("div", { class: "shell" }, header, barEl, mainEl, footer(me.build)));
|
||||
modeHolder = modeEl;
|
||||
renderNav();
|
||||
renderBar();
|
||||
@@ -170,6 +169,16 @@ function layout() {
|
||||
|
||||
let modeHolder;
|
||||
|
||||
// footer names the Jarl build: the release, or the commit for other
|
||||
// builds, linked to it in the repository.
|
||||
function footer(b) {
|
||||
if (!b) return null;
|
||||
const what = b.tag ? "release " + b.tag
|
||||
: b.commit ? "commit " + b.commit + (b.modified ? ", with uncommitted changes" : "") : "version " + b.version;
|
||||
return h("footer", { class: "site" }, "Powered by ",
|
||||
h("a", { href: b.url, target: "_blank", rel: "noopener", title: "Jarl " + b.version + ", built from " + what }, "Jarl " + b.label));
|
||||
}
|
||||
|
||||
function renderNav() {
|
||||
mount(navEl, navItems.map(([key, href, label]) => h("a", { href, class: current.nav === key ? "active" : "" }, label)));
|
||||
}
|
||||
@@ -211,12 +220,21 @@ async function route() {
|
||||
const params = {};
|
||||
(r.params || []).forEach((p, i) => { params[p] = m[i + 1]; });
|
||||
current = { key: path, nav: r.nav, page: r.page, params, query };
|
||||
if (!r.params && r.nav) lastList[r.nav] = location.hash;
|
||||
renderNav();
|
||||
mount(mainEl, h("div", { class: "muted" }, "Loading…"));
|
||||
// Keep the old page until the new one is drawn (switching a zone's
|
||||
// tabs shouldn't blank the page); say "Loading…" only when it's slow.
|
||||
// (Only while the new page has drawn nothing yet: pages that draw
|
||||
// their frame first fill it in afterwards.)
|
||||
const gen = ++routeGen;
|
||||
const old = mainEl.firstChild;
|
||||
const slow = setTimeout(() => { if (gen === routeGen && mainEl.firstChild === old) mount(mainEl, h("div", { class: "muted" }, "Loading…")); }, 300);
|
||||
try {
|
||||
await r.page.render(mainEl, params, query);
|
||||
} catch (e) {
|
||||
mount(mainEl, h("h1", null, "Something went wrong"), errorBox(e));
|
||||
if (gen === routeGen) mount(mainEl, h("h1", null, "Something went wrong"), errorBox(e));
|
||||
} finally {
|
||||
clearTimeout(slow);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user