diff --git a/CHANGELOG.md b/CHANGELOG.md index 00a4677..20ea3b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,12 @@ All notable changes to Jarl are documented here. The format follows ### Fixed +- Option values in the web UI looked different from the editor: a long + line wrapped back to the left edge, losing its indentation, and the + column changed width when Edit opened. Values now keep their layout + (a long line scrolls), with the same width and padding as the editor. + The "{ } around the list are optional" hint only shows for options that + end in a list (not rate-limit, which only has one inside). - Query statistics skipped every query signed with a TSIG key (named writes "/key NAME" after the client's port), such as queries with a view's key: they are counted now. diff --git a/internal/web/static/assets/pages/options.js b/internal/web/static/assets/pages/options.js index c561369..8323dc0 100644 --- a/internal/web/static/assets/pages/options.js +++ b/internal/web/static/assets/pages/options.js @@ -31,9 +31,13 @@ function valueWidget(stmt, value, ctx) { const t = codeArea(value || ""); // Statements such as allow-transfer and listen-on take an address // match list after optional parameters: suggest names at the cursor. - const takesList = stmt && /address_match_element/.test(stmt.grammar || ""); + // Names are suggested wherever the grammar has an address match list + // (rate-limit's exempt-clients too); the braces are only optional for + // statements that end in the list (catalog WrapList). + const hasAML = stmt && /address_match_element/.test(stmt.grammar || ""); + const takesList = stmt && /\{ ; \.\.\. \};$/.test((stmt.grammar || "").split("\n")[0]); if (takesList && !value) t.placeholder = "homelab; key xfer-key"; - return { el: h("div", null, t, takesList && ctx ? suggestions(t, ctx, { mode: "inline" }) : null), get: () => t.value.trim(), + return { el: h("div", null, t, hasAML && ctx ? suggestions(t, ctx, { mode: "inline" }) : null), get: () => t.value.trim(), hint: takesList ? "The { } around the list are optional: 10.0.0.53; key xfer-key becomes { 10.0.0.53; key xfer-key; }." : null }; } const i = input({ value: value || "", class: "mono" }); @@ -83,7 +87,7 @@ export async function optionsEditor(el, { scope, scope_id, catalogScope, title, const draw = () => { mount(listEl, table([ { label: "Option", render: (o) => h("span", null, h("strong", { class: "mono" }, o.name), o.status ? [" ", badge(o.status, "warn")] : null), tdClass: "nowrap" }, - { label: "Value", render: (o) => editing === o.id ? editRow(o) : h("pre", { class: "code", style: { padding: "2px 6px", whiteSpace: "pre-wrap" } }, o.formatted || o.value || "(no value)") }, + { label: "Value", render: (o) => editing === o.id ? editRow(o) : h("pre", { class: "code optval" }, o.formatted || o.value || "(no value)") }, { label: "Description", render: (o) => h("span", { class: "muted small" }, o.description || ""), tdClass: "small" }, { label: "", render: (o) => editing === o.id ? "" : h("span", null, h("button", { class: "small", onclick: () => { editing = o.id; draw(); } }, "Edit"), " ", diff --git a/internal/web/static/assets/style.css b/internal/web/static/assets/style.css index 1b2d1a4..1d548ef 100644 --- a/internal/web/static/assets/style.css +++ b/internal/web/static/assets/style.css @@ -501,3 +501,11 @@ ul.plain li { padding: 3px 0; } a.card.link { color: inherit; text-decoration: none; } a.card.link:hover { border-color: var(--accent, var(--link)); } + +/* An option's value as shown: laid out like the editor (no wrapping, a + long line scrolls; same padding and an invisible border), so the text + stays put when Edit opens it. */ +pre.optval { padding: 5px 8px; border: 1px solid transparent; background: none; white-space: pre; overflow-x: auto; max-width: min(72ch, 50vw); } +/* ...and the editor in a table cell gets the same width, so the column + doesn't jump when Edit opens. */ +td textarea.code { min-width: min(72ch, 50vw); }