diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e89f7..c680454 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version ## [Unreleased] +### Fixed + +- **Log panes wrap their lines instead of scrolling sideways**: a phone drew + two scroll bars over the Delivery log of a message, and the end of a + delivery line - `status=` and the reply - was out of sight. A long line + wraps, the rest indented under its start; copied text keeps one line per + line. The Delivery log, System log and the queue's listing. + ## [2.9.0] - 2026-10-16 The panel's DNS records read faster: a record DNS already holds as shown diff --git a/internal/web/view/components_test.go b/internal/web/view/components_test.go index 0d53778..3eb9109 100644 --- a/internal/web/view/components_test.go +++ b/internal/web/view/components_test.go @@ -364,17 +364,19 @@ func TestTimelineLevels(t *testing.T) { `
  • Still queued
  • `) } -// The log is a pre: its lines are separated by a newline of their own, not by -// the template's layout whitespace, and a line is escaped. +// The log is a pre of lines: each a block of its own (sp-ll), which wraps +// under its start rather than scrolling sideways, ended by a newline inside it +// so copied text keeps one line per line - and nothing of the template's +// layout whitespace between them. A line is escaped. func TestLogPaneIsAPreWithEscapedLines(t *testing.T) { out := partial(t, "log_pane", []LogLine{ {Time: "13:58:07", Text: "message-id="}, {Time: "13:58:09", Text: "status=deferred", Level: "warn"}, {Text: "no time on this one", Level: "error"}, }) - want := `
    13:58:07 message-id=<a81f0c@alertmanager>` + "\n" +
    -		`13:58:09 status=deferred` + "\n" +
    -		`no time on this one
    ` + want := `
    13:58:07 message-id=<a81f0c@alertmanager>` + "\n" +
    +		`13:58:09 status=deferred` + "\n" +
    +		`no time on this one` + "\n
    " if out != want { t.Errorf("log_pane =\n%s\nwant\n%s", out, want) } diff --git a/internal/web/view/static/panel.css b/internal/web/view/static/panel.css index 1c8d4e9..23e55ae 100644 --- a/internal/web/view/static/panel.css +++ b/internal/web/view/static/panel.css @@ -295,8 +295,12 @@ details.sp-record[open] .sp-record-toggle span + span { display: inline; } /* ---- logs, timeline, help */ .sp-log { margin: 0; padding: 1rem 1.25rem; background: #191c21; color: #d9d5cc; border-radius: 0; - font-family: var(--sp-mono); font-size: var(--sp-fs-s); line-height: 1.6; overflow-x: auto; white-space: pre; + font-family: var(--sp-mono); font-size: var(--sp-fs-s); line-height: 1.6; white-space: pre-wrap; overflow-wrap: anywhere; } +/* One line of a log: long ones wrap inside the pane, the rest of the line + indented under its start, so nothing scrolls sideways - a phone drew the + scroll bars over the text (owner, 2026-10-16). */ +.sp-log .sp-ll { display: block; padding-left: 4ch; text-indent: -4ch; } .sp-log .sp-w { color: #e0b15c; } .sp-log .sp-e { color: #ef8f82; } .sp-log .sp-d { color: #7f8894; } .sp-timeline { list-style: none; margin: 0; padding: 0; } .sp-timeline li { position: relative; padding: 0 0 1.1rem 1.6rem; border-left: 2px solid var(--sp-rule); margin-left: .4rem; } diff --git a/internal/web/view/templates/components.html b/internal/web/view/templates/components.html index 428960a..88a43df 100644 --- a/internal/web/view/templates/components.html +++ b/internal/web/view/templates/components.html @@ -190,8 +190,8 @@ {{/* log_pane is a dark block of log lines (input: []LogLine). The time is dimmed, a warning amber, an error red. The newline between lines is part of the text: the pane is a pre. */}} -{{define "log_pane"}}
    {{range $i, $l := .}}{{if $i}}
    -{{end}}{{if $l.Time}}{{$l.Time}} {{end}}{{if eq $l.Level "warn"}}{{$l.Text}}{{else if eq $l.Level "error"}}{{$l.Text}}{{else}}{{$l.Text}}{{end}}{{end}}
    {{end}} +{{define "log_pane"}}
    {{range .}}{{if .Time}}{{.Time}} {{end}}{{if eq .Level "warn"}}{{.Text}}{{else if eq .Level "error"}}{{.Text}}{{else}}{{.Text}}{{end}}
    +{{end}}
    {{end}} {{/* empty_state says what is absent and why that is fine (input: EmptyState). */}} {{define "empty_state"}}
    {{template "inline" .Text}}
    {{end}}