This commit is contained in:
ponzischeme89
2026-08-18 08:41:48 +12:00
parent 1da91e40a1
commit 36d171e51b
50 changed files with 4972 additions and 377 deletions
+513 -105
View File
@@ -22,7 +22,12 @@
which is what lets a page distinguish one kind of thing from another without every
coloured element reading as a warning. Green means good, amber means look, red means
wrong; the other three mean nothing at all, which is the point. A tone is passed, never
derived: the library is teal, a person violet, a television blue, on every page. */
derived: the library is teal, a person violet, a television blue, on every page.
Beside the accent is one quieter green, `idle`, which is not a seventh meaning but the
accent's meaning held at lower confidence: good, but not this minute. It exists so that
"fine and not connected right now" can stop borrowing amber, which is reserved for
something an operator should look at. Nothing derives it either. */
:root {
color-scheme: dark;
@@ -41,6 +46,13 @@
--accent: #2ea043;
--accent-ink: #56d364;
--accent-wash: rgba(46, 160, 67, 0.16);
/* The quiet green: fine, but not this minute. Same hue as the accent and deliberately
duller, because it is the accent's own meaning at lower confidence rather than a
verdict of its own — a television in ordinary use that happens to be switched off is
not something an operator has to do anything about, and amber said it was. */
--idle: #4a8f60;
--idle-ink: #86c79a;
--idle-wash: rgba(74, 143, 96, 0.13);
--danger: #e5534b;
--danger-ink: #ff9b94;
--danger-wash: rgba(229, 83, 75, 0.13);
@@ -1030,6 +1042,10 @@ a.tile:hover {
background: var(--accent-wash);
color: var(--accent-ink);
}
.glyph[data-tone="idle"] {
background: var(--idle-wash);
color: var(--idle-ink);
}
.glyph[data-tone="warn"] {
background: var(--warn-wash);
color: var(--warn-ink);
@@ -1078,6 +1094,11 @@ a.tile:hover {
background: var(--accent-wash);
color: var(--accent-ink);
}
.tag[data-tone="idle"] {
border-color: rgba(74, 143, 96, 0.35);
background: var(--idle-wash);
color: var(--idle-ink);
}
.tag[data-tone="warn"] {
border-color: rgba(239, 196, 107, 0.35);
background: var(--warn-wash);
@@ -1119,6 +1140,9 @@ a.tile:hover {
.chip[data-tone="ok"] {
color: var(--accent-ink);
}
.chip[data-tone="idle"] {
color: var(--idle-ink);
}
.chip[data-tone="warn"] {
color: var(--warn-ink);
}
@@ -1744,6 +1768,109 @@ select {
/* ---------- log / code ---------- */
/* The log table.
Six columns, in the order the questions are asked: when, how bad, which part of the
server, what happened, did it work, how long did it take. The columns are the whole
design — an operator learns where to look once and then reads down a column rather than
across a line, which is what makes thirty seconds of watching this page worth anything.
Monospace is spent deliberately rather than applied to the row. The time, the method,
the status and the duration are values to be compared down a column and are set in it;
the summary is a sentence and is set in the sans face, because a sentence in monospace
is slower to read and looks like a dump of something. */
.logbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 10px;
margin-bottom: 10px;
}
.logbar-filters {
display: flex;
flex: 1 1 520px;
flex-wrap: wrap;
align-items: center;
gap: 6px;
min-width: 0;
}
.logbar-filters select {
width: auto;
min-width: 0;
height: 30px;
padding: 0 26px 0 9px;
font-size: 12px;
}
/* The actions are secondary to the filters and are marked so by weight, not by distance:
the previous bar gave Pause and Export the same prominence as the controls that decide
what the table contains. */
.logbar-actions {
display: flex;
flex: 0 0 auto;
gap: 6px;
margin-left: auto;
}
.logsearch {
position: relative;
display: flex;
flex: 1 1 260px;
align-items: center;
min-width: 200px;
}
.logsearch svg {
position: absolute;
left: 9px;
width: 14px;
height: 14px;
color: var(--quiet);
pointer-events: none;
}
.logsearch input {
width: 100%;
height: 30px;
padding-left: 28px;
font-size: 12px;
}
/* One chip per narrowing in force. They are the only place a filter can be removed one at
a time, which is what makes drilling in by clicking a value in the table reversible. */
.logchips {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 10px;
}
.logchip {
display: inline-flex;
align-items: center;
gap: 5px;
height: 24px;
padding: 0 8px;
border: 1px solid var(--line);
border-radius: 999px;
background: var(--surface-lift);
color: var(--muted);
font: 500 11px/1 var(--sans);
}
.logchip svg {
width: 11px;
height: 11px;
opacity: .7;
}
.logchip:hover:not(:disabled) {
border-color: var(--danger);
background: var(--danger-wash);
color: var(--danger-ink);
}
.logchip-clear {
border-style: dashed;
color: var(--quiet);
}
.logshell {
position: relative;
}
.logview {
height: 62vh;
min-height: 320px;
@@ -1751,17 +1878,24 @@ select {
border: 1px solid var(--line);
border-radius: var(--radius-sm);
background: #080a0e;
font: 12px/1.6 var(--mono);
font: 12px/1.5 var(--sans);
contain: layout paint style;
}
.loghead,
.logrow {
display: grid;
grid-template-columns:
92px 52px minmax(150px, 190px) minmax(240px, 1fr)
minmax(96px, 150px) 68px;
gap: 12px;
padding: 0 12px;
}
.loghead {
position: sticky;
top: 0;
z-index: 1;
display: grid;
grid-template-columns: 150px 46px minmax(180px, 1fr) minmax(240px, 2fr);
gap: 12px;
padding: 7px 12px;
z-index: 2;
align-items: center;
height: 31px;
border-bottom: 1px solid var(--line);
background: #10131a;
color: var(--quiet);
@@ -1769,142 +1903,413 @@ select {
font-weight: 700;
letter-spacing: .08em;
text-transform: uppercase;
min-width: 760px;
min-width: 900px;
}
.loghead > :last-child {
text-align: right;
}
.logbody {
position: relative;
min-width: 760px;
min-width: 900px;
}
.logline {
display: flex;
gap: 12px;
padding: 3px 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.logline[data-virtual="true"] {
/* The date is a divider between days rather than a column repeated on every row. A log
watched live is almost always one day; printing the date 20,000 times to cover the
handful of rows where it changes is the trade the compact timestamp exists to avoid. */
.logday {
position: absolute;
top: 0;
right: 0;
left: 0;
display: grid;
grid-template-columns: 150px 46px minmax(180px, 1fr) minmax(240px, 2fr);
display: flex;
align-items: center;
height: 48px;
padding-top: 4px;
padding-bottom: 4px;
white-space: nowrap;
height: 26px;
padding: 0 12px;
background: linear-gradient(to bottom, rgba(255, 255, 255, .03), transparent);
color: var(--quiet);
font: 700 10px/1 var(--sans);
letter-spacing: .08em;
text-transform: uppercase;
}
.logday span {
padding-right: 10px;
background: #080a0e;
}
.logday::after {
content: '';
flex: 1;
height: 1px;
background: var(--line-soft);
}
.logrow {
position: absolute;
top: 0;
right: 0;
left: 0;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, .03);
contain: strict;
}
.logline:hover {
background: rgba(255, 255, 255, 0.03);
.logrow:hover {
background: rgba(255, 255, 255, .035);
}
.logline time {
flex: 0 0 auto;
.logrow[data-selected] {
background: var(--accent-wash);
}
/* A failure is marked on the row and not only in a column, because the thing an operator
is scanning for is a row, and a hairline at the start of one is found faster than a word
two thirds of the way along it. INFO is deliberately unmarked: most of the table is INFO
and a mark every row carries is a mark that says nothing. */
.logrow[data-level="ERROR"] {
box-shadow: inset 2px 0 0 var(--danger);
background: rgba(229, 83, 75, .05);
}
.logrow[data-level="WARN"] {
box-shadow: inset 2px 0 0 var(--warn);
}
.logrow-time {
color: var(--quiet);
font: 11px/1 var(--mono);
font-variant-numeric: tabular-nums;
}
.logline time small {
display: block;
color: var(--muted);
font-size: 10px;
}
.logline .lvl {
flex: 0 0 46px;
font-weight: 700;
}
.logline[data-level="ERROR"] .lvl {
color: var(--danger-ink);
}
.logline[data-level="WARN"] .lvl {
color: var(--warn-ink);
}
.logline[data-level="INFO"] .lvl {
color: var(--info-ink);
}
.logline[data-level="DEBUG"] .lvl {
color: var(--quiet);
}
.logline .msg {
flex: 1 1 auto;
/* Every structured value in the table is also the control that filters to it. They are
buttons rather than links because nothing navigates: the table narrows in place. */
.logfacet {
height: auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.logline .attrs {
color: var(--quiet);
}
@media (min-width: 900px) {
.logline {
display: grid;
grid-template-columns: 150px 46px minmax(180px, 1fr) minmax(240px, 2fr);
}
}
.logline .attrs b {
color: var(--muted);
font-weight: 400;
}
.logattrs-button {
display: block;
width: 100%;
min-width: 0;
height: 28px;
padding: 0;
overflow: hidden;
border: 0;
background: transparent;
color: var(--quiet);
background: none;
color: inherit;
font: inherit;
text-align: left;
text-overflow: ellipsis;
white-space: nowrap;
}
.logattrs-button:hover:not(:disabled) {
.logfacet:hover:not(:disabled) {
border: 0;
background: transparent;
color: var(--text);
background: none;
text-decoration: underline;
text-underline-offset: 3px;
}
.logattrs-button:focus-visible {
.logfacet:focus-visible {
outline: 1px solid var(--accent);
outline-offset: 2px;
}
.log-inspector {
.logrow-level {
color: var(--quiet);
font: 700 10px/1 var(--sans);
letter-spacing: .06em;
}
.logrow-level[data-level="ERROR"] { color: var(--danger-ink); }
.logrow-level[data-level="WARN"] { color: var(--warn-ink); }
.logrow-level[data-level="INFO"] { color: var(--muted); }
.logrow-level[data-level="DEBUG"],
.logrow-level[data-level="TRACE"] { color: var(--quiet); }
/* Service and component. The service is the recognisable half — one word, upper case,
always the same colour for the same subsystem — and the component sits beside it in the
ordinary text colour, so the pair reads as a place rather than as two tags. The tones
are the console's secondary palette only: green, amber and red mean good, look and wrong
on every other page and must not start meaning "playback" on this one. */
.logrow-place {
display: flex;
align-items: baseline;
gap: 5px;
min-width: 0;
}
.logrow-service {
flex: 0 0 auto;
max-width: 96px;
overflow: hidden;
color: var(--quiet);
font: 700 10px/1.4 var(--sans);
letter-spacing: .07em;
text-overflow: ellipsis;
text-transform: uppercase;
white-space: nowrap;
}
.logrow-service[data-tone="info"] { color: var(--info-ink); }
.logrow-service[data-tone="note"] { color: var(--note-ink); }
.logrow-service[data-tone="data"] { color: var(--data-ink); }
.logrow-service[data-tone="idle"] { color: var(--idle-ink); }
.logrow-service[data-tone="quiet"] { color: var(--muted); }
.logrow-sep {
flex: 0 0 auto;
color: var(--line);
}
.logrow-component {
flex: 1 1 auto;
color: var(--quiet);
font-size: 11.5px;
}
/* The summary is the row. It is a button because selecting the row is what opens the
record, and the whole width of the column should be the target. */
.logrow-summary {
display: flex;
flex-direction: column;
gap: 2px;
justify-content: center;
width: 100%;
height: 100%;
min-width: 0;
padding: 0;
overflow: hidden;
border: 0;
background: none;
font: inherit;
text-align: left;
}
.logrow-summary:hover:not(:disabled) {
border: 0;
background: none;
}
.logrow-summary:focus-visible {
outline: 1px solid var(--accent);
outline-offset: -1px;
}
.logrow-line {
display: flex;
align-items: baseline;
gap: 8px;
min-width: 0;
}
.logrow-action {
flex: 0 0 auto;
color: var(--muted);
font: 600 10.5px/1.4 var(--mono);
letter-spacing: .04em;
}
.logrow-action[data-method="POST"],
.logrow-action[data-method="PUT"],
.logrow-action[data-method="PATCH"] { color: var(--info-ink); }
.logrow-action[data-method="DELETE"] { color: var(--danger-ink); }
.logrow-text {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
color: var(--text);
text-overflow: ellipsis;
white-space: nowrap;
}
/* The reason a failure needs no drawer. */
.logrow-error {
overflow: hidden;
color: var(--danger-ink);
font-size: 11px;
text-overflow: ellipsis;
white-space: nowrap;
}
.logrow-context {
overflow: hidden;
color: var(--quiet);
font-size: 11px;
text-overflow: ellipsis;
white-space: nowrap;
}
/* A result is a word, not a badge. A success is understated to the point of being ignorable
— which is the correct amount of attention for the four hundredth 200 in a row — and only
a failure is given a fill. */
.logrow-result {
min-width: 0;
}
.logrow-verdict {
display: inline-block;
max-width: 100%;
color: var(--quiet);
font: 500 11px/1.5 var(--mono);
}
.logrow-verdict[data-tone="ok"] { color: var(--muted); }
.logrow-verdict[data-tone="info"] { color: var(--info-ink); }
.logrow-verdict[data-tone="data"] { color: var(--data-ink); }
.logrow-verdict[data-tone="warn"],
.logrow-verdict[data-tone="bad"] {
padding: 1px 6px;
border-radius: var(--radius-xs);
font-weight: 600;
}
.logrow-verdict[data-tone="warn"] {
background: var(--warn-wash);
color: var(--warn-ink);
}
.logrow-verdict[data-tone="bad"] {
background: var(--danger-wash);
color: var(--danger-ink);
}
.logrow-duration {
color: var(--quiet);
font: 11px/1 var(--mono);
font-variant-numeric: tabular-nums;
text-align: right;
}
.logrow-duration[data-tone="warn"] { color: var(--warn-ink); }
.logrow-duration[data-tone="bad"] {
color: var(--danger-ink);
font-weight: 600;
}
/* Offered only once the operator has left the tail, which is the only time following the
log automatically would be taking the page away from them. */
.logtail {
position: absolute;
right: 18px;
bottom: 14px;
z-index: 3;
display: inline-flex;
align-items: center;
gap: 6px;
height: 28px;
padding: 0 12px;
border: 1px solid var(--accent);
border-radius: 999px;
background: var(--surface-lift);
color: var(--accent-ink);
font: 600 11px/1 var(--sans);
box-shadow: 0 6px 16px rgba(0, 0, 0, .5);
}
.logtail svg {
width: 12px;
height: 12px;
transform: rotate(90deg);
}
.logtail:hover:not(:disabled) {
border-color: var(--accent);
background: var(--accent-wash);
color: var(--accent-ink);
}
/* The drawer answers "exactly how and why". Sections rather than one alphabetical list,
and a field with no value is omitted rather than printed as `unknown`. */
.logdrawer {
margin-top: 12px;
padding: 12px;
padding: 14px;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
background: var(--surface);
}
.log-inspector-head {
.logdrawer-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
padding-bottom: 12px;
border-bottom: 1px solid var(--line);
}
.log-inspector-head > div {
.logdrawer-head > div:first-child {
min-width: 0;
}
.log-inspector-head b,
.log-inspector-head span {
.logdrawer-head b {
display: block;
margin-top: 4px;
font-size: 14px;
overflow-wrap: anywhere;
}
.log-inspector-head span {
margin-top: 3px;
.logdrawer-place {
display: flex;
align-items: baseline;
gap: 5px;
margin: 0;
color: var(--quiet);
font: 11px/1.5 var(--mono);
font-size: 11px;
}
.log-inspector dl {
.logdrawer-error {
margin: 6px 0 0;
color: var(--danger-ink);
font: 12px/1.5 var(--mono);
overflow-wrap: anywhere;
}
.logdrawer-actions {
display: flex;
flex: 0 0 auto;
gap: 6px;
}
.logdrawer-grid {
display: grid;
grid-template-columns: minmax(130px, max-content) minmax(0, 1fr);
gap: 5px 14px;
margin: 12px 0 0;
padding-top: 10px;
border-top: 1px solid var(--line);
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px 24px;
margin-top: 12px;
}
.logdrawer-section h4 {
margin: 0 0 6px;
color: var(--quiet);
font: 700 10px/1 var(--sans);
letter-spacing: .08em;
text-transform: uppercase;
}
.logdrawer-section dl {
display: grid;
grid-template-columns: minmax(88px, max-content) minmax(0, 1fr);
gap: 4px 12px;
margin: 0;
font: 12px/1.5 var(--mono);
}
.log-inspector dt { color: var(--muted); }
.log-inspector dd { min-width: 0; margin: 0; overflow-wrap: anywhere; }
.logdrawer-section dt { color: var(--muted); }
.logdrawer-section dd { min-width: 0; margin: 0; overflow-wrap: anywhere; }
.logdrawer-raw {
margin-top: 14px;
padding-top: 12px;
border-top: 1px solid var(--line);
}
.logdrawer-raw summary {
cursor: pointer;
color: var(--quiet);
font: 700 10px/1 var(--sans);
letter-spacing: .08em;
text-transform: uppercase;
}
.logdrawer-raw pre {
max-height: 320px;
margin: 10px 0 0;
overflow: auto;
padding: 10px;
border: 1px solid var(--line);
border-radius: var(--radius-xs);
background: #080a0e;
font: 11px/1.6 var(--mono);
}
/* Columns are given up in the order they are least missed: the duration first, since it is
the only one of the six whose absence costs nothing to understanding what happened, and
the timestamp after it. Level, service and the summary are never dropped — a log that
cannot say how bad, where, or what is not a log. */
@media (max-width: 1180px) {
.loghead,
.logrow {
grid-template-columns: 84px 46px minmax(130px, 170px) minmax(200px, 1fr) minmax(88px, 130px);
gap: 10px;
}
.loghead > :last-child,
.logrow-duration {
display: none;
}
.loghead,
.logbody {
min-width: 640px;
}
}
@media (max-width: 900px) {
.loghead,
.logrow {
grid-template-columns: 46px minmax(110px, 140px) minmax(180px, 1fr) minmax(72px, 110px);
gap: 8px;
}
.loghead > :first-child,
.logrow-time {
display: none;
}
.loghead,
.logbody {
min-width: 520px;
}
}
.logdetails {
min-width: 0;
}
@@ -2243,7 +2648,11 @@ pre.code {
letter-spacing: 0.02em;
}
/* The presence dot: three-valued, and the tone is passed rather than derived. */
/* The presence dot: four-valued, and the tone is passed rather than derived. The two
greens are the point — one set is connected now and the other was an hour ago, and both
are fine, so they read as the same answer at two strengths rather than as two answers.
The dot is 7px and colour alone is never the whole signal here: every one of these
carries the wording in its title. */
.dot-state {
width: 7px;
height: 7px;
@@ -2254,6 +2663,9 @@ pre.code {
.dot-state[data-tone="ok"] {
background: var(--accent);
}
.dot-state[data-tone="idle"] {
background: var(--idle);
}
.dot-state[data-tone="warn"] {
background: var(--warn);
}
@@ -2873,18 +3285,14 @@ details summary {
min-height: 60px;
padding: 13px 0;
}
.loghead,
.logbody {
min-width: 680px;
}
.loghead,
.logline[data-virtual="true"] {
grid-template-columns: 128px 44px minmax(160px, 1fr) minmax(220px, 1.4fr);
gap: 10px;
}
/* The column collapse itself is width-driven and lives beside the table's own rules;
what a touch device needs on top of it is room to scroll and controls it can reach. */
.logview {
height: min(66dvh, 720px);
}
.logbar-actions {
margin-left: 0;
}
}
@media (max-width: 820px) {