Web UI: option values keep the editor's layout
A shown option value wrapped long lines back to the left edge, losing
their indentation, and had different padding and width from the editor,
so the text and column moved when Edit opened. Values now don't wrap (a
long line scrolls), with the editor's padding and the same width. The
"{ } around the list are optional" hint only shows for options that end
in a list, not rate-limit, which only has one inside.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bad360107e
commit
88965096bb
3 files changed
+21
-3
No files matched your search
@@ -20,6 +20,12 @@ All notable changes to Jarl are documented here. The format follows
|
|||||||
|
|
||||||
### Fixed
|
### 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
|
- Query statistics skipped every query signed with a TSIG key (named
|
||||||
writes "/key NAME" after the client's port), such as queries with a
|
writes "/key NAME" after the client's port), such as queries with a
|
||||||
view's key: they are counted now.
|
view's key: they are counted now.
|
||||||
|
|||||||
@@ -31,9 +31,13 @@ function valueWidget(stmt, value, ctx) {
|
|||||||
const t = codeArea(value || "");
|
const t = codeArea(value || "");
|
||||||
// Statements such as allow-transfer and listen-on take an address
|
// Statements such as allow-transfer and listen-on take an address
|
||||||
// match list after optional parameters: suggest names at the cursor.
|
// 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 && /\{ <address_match_element>; \.\.\. \};$/.test((stmt.grammar || "").split("\n")[0]);
|
||||||
if (takesList && !value) t.placeholder = "homelab; key xfer-key";
|
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 };
|
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" });
|
const i = input({ value: value || "", class: "mono" });
|
||||||
@@ -83,7 +87,7 @@ export async function optionsEditor(el, { scope, scope_id, catalogScope, title,
|
|||||||
const draw = () => {
|
const draw = () => {
|
||||||
mount(listEl, table([
|
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: "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: "Description", render: (o) => h("span", { class: "muted small" }, o.description || ""), tdClass: "small" },
|
||||||
{ label: "", render: (o) => editing === o.id ? "" : h("span", null,
|
{ label: "", render: (o) => editing === o.id ? "" : h("span", null,
|
||||||
h("button", { class: "small", onclick: () => { editing = o.id; draw(); } }, "Edit"), " ",
|
h("button", { class: "small", onclick: () => { editing = o.id; draw(); } }, "Edit"), " ",
|
||||||
|
|||||||
@@ -501,3 +501,11 @@ ul.plain li { padding: 3px 0; }
|
|||||||
|
|
||||||
a.card.link { color: inherit; text-decoration: none; }
|
a.card.link { color: inherit; text-decoration: none; }
|
||||||
a.card.link:hover { border-color: var(--accent, var(--link)); }
|
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); }
|
||||||
Reference in new issue
Block a user