fix console log time and server card layout

This commit is contained in:
npc0-hue
2026-08-24 11:06:18 +08:00
parent 878f26c947
commit b69939a5ce
5 changed files with 42 additions and 4 deletions
@@ -6,6 +6,7 @@ import type { JobResponse, LogEntryBody, LogStreamResponse } from "../api/types"
import { scumSourceRCONCommandRequest } from "../schemas/scumManagementRcon"; import { scumSourceRCONCommandRequest } from "../schemas/scumManagementRcon";
import { cx } from "../utils/classes"; import { cx } from "../utils/classes";
import { mergeLogStreams, parseLogSessionEvent, parseLogStreamEvent, parseServerLogEvent, streamFromServerLogEvent } from "../utils/logEvents"; import { mergeLogStreams, parseLogSessionEvent, parseLogStreamEvent, parseServerLogEvent, streamFromServerLogEvent } from "../utils/logEvents";
import { formatTerminalLogTime } from "../utils/logTime";
import { EmptyState, ResultBadge } from "./StateViews"; import { EmptyState, ResultBadge } from "./StateViews";
type LoadState<T> = { status: "loading" } | { status: "error"; reason: string } | { status: "ready"; data: T }; type LoadState<T> = { status: "loading" } | { status: "error"; reason: string } | { status: "ready"; data: T };
@@ -410,7 +411,7 @@ function terminalLineFromLog(stream: LogStreamResponse, entry: LogEntryBody): Te
id: `log-${stream.id}-${entry.seq}`, id: `log-${stream.id}-${entry.seq}`,
tone: terminalTone(entry), tone: terminalTone(entry),
text: entry.line, text: entry.line,
at: new Date(entry.timestamp).toLocaleTimeString(), at: formatTerminalLogTime(entry.timestamp, entry.line),
sortKey: Date.parse(entry.timestamp) || Date.now(), sortKey: Date.parse(entry.timestamp) || Date.now(),
streamKey: stream.streamKey || stream.source, streamKey: stream.streamKey || stream.source,
level: entry.level, level: entry.level,
+8
View File
@@ -58,6 +58,14 @@ describe("platform web shared theme CSS", () => {
expect(css).toContain(":root[data-custom-background=true].server-card-stat"); expect(css).toContain(":root[data-custom-background=true].server-card-stat");
}); });
it("gives server cards enough room for long status labels", () => {
const css = compact(readThemeCss());
expect(css).toContain(".server-card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,420px),1fr))");
expect(css).toContain(".server-card-head{display:grid;gap:8px;min-width:0}");
expect(css).toContain(".server-card-head>span:last-child{justify-self:start;white-space:normal");
});
it("keeps magical custom-background signal rows readable without the jelly wash", () => { it("keeps magical custom-background signal rows readable without the jelly wash", () => {
const css = compact(readThemeCss()); const css = compact(readThemeCss());
+7 -3
View File
@@ -450,11 +450,13 @@ to{transform:translate(-50%,-50%) rotate(calc(var(--construct-drift) + 360deg))}
.server-toolbar select{min-height:38px;border:1px solid var(--line-strong);border-radius:8px;padding:0 10px;background:var(--surface-solid);color:var(--ink);font:inherit;min-width:180px} .server-toolbar select{min-height:38px;border:1px solid var(--line-strong);border-radius:8px;padding:0 10px;background:var(--surface-solid);color:var(--ink);font:inherit;min-width:180px}
.server-toolbar select:focus{border-color:var(--accent);outline:2px solid var(--accent-soft)} .server-toolbar select:focus{border-color:var(--accent);outline:2px solid var(--accent-soft)}
.server-toolbar input[type=search]:focus{border-color:var(--accent);outline:2px solid var(--accent-soft)} .server-toolbar input[type=search]:focus{border-color:var(--accent);outline:2px solid var(--accent-soft)}
.server-card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(310px,1fr));gap:14px} .server-card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,420px),1fr));gap:16px}
.server-card{display:grid;gap:12px;padding:16px;border:1px solid var(--line);border-radius:8px;background:var(--frosted-surface),var(--glass-tint),var(--surface);backdrop-filter:blur(22px) saturate(1.28);text-align:left;transition:transform 120ms ease,border-color 120ms ease;box-shadow:var(--jelly-inset),inset 0 0 0 1px var(--diamond-line),0 18px 42px var(--glass-shadow),0 0 28px rgba(255,255,255,.2);position:relative;overflow:hidden;min-width:0} .server-card{display:grid;gap:12px;padding:16px;border:1px solid var(--line);border-radius:8px;background:var(--frosted-surface),var(--glass-tint),var(--surface);backdrop-filter:blur(22px) saturate(1.28);text-align:left;transition:transform 120ms ease,border-color 120ms ease;box-shadow:var(--jelly-inset),inset 0 0 0 1px var(--diamond-line),0 18px 42px var(--glass-shadow),0 0 28px rgba(255,255,255,.2);position:relative;overflow:hidden;min-width:0}
.server-card:focus-visible,.server-card:hover{border-color:var(--accent);outline:0;transform:translateY(-2px)} .server-card:focus-visible,.server-card:hover{border-color:var(--accent);outline:0;transform:translateY(-2px)}
.server-card-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px} .server-card-head{display:grid;gap:8px;min-width:0}
.server-card-head>span:first-child{min-width:0} .server-card-head>span:first-child,.server-card-head>span:last-child{min-width:0;max-width:100%}
.server-card-head>span:last-child{justify-self:start;white-space:normal;line-height:1.3}
.server-card>*{min-width:0}
.server-card-head strong{display:block;font-size:16px;color:var(--ink);overflow-wrap:anywhere} .server-card-head strong{display:block;font-size:16px;color:var(--ink);overflow-wrap:anywhere}
.server-card-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px} .server-card-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
.server-card-stat{display:grid;gap:2px;padding:8px;border:1px solid color-mix(in srgb,var(--line-strong) 56%,rgba(255,255,255,.16));border-radius:8px;background:linear-gradient(180deg,color-mix(in srgb,var(--surface-solid) 88%,rgba(255,255,255,.06)),color-mix(in srgb,var(--surface-solid) 72%,var(--accent-soft))),var(--glass-wash);min-height:52px;align-content:center;box-shadow:inset 0 1px 0 color-mix(in srgb,var(--crystal-rim) 68%,transparent),0 8px 18px rgba(0,0,0,.18);min-width:0;backdrop-filter:blur(10px) saturate(0.92)} .server-card-stat{display:grid;gap:2px;padding:8px;border:1px solid color-mix(in srgb,var(--line-strong) 56%,rgba(255,255,255,.16));border-radius:8px;background:linear-gradient(180deg,color-mix(in srgb,var(--surface-solid) 88%,rgba(255,255,255,.06)),color-mix(in srgb,var(--surface-solid) 72%,var(--accent-soft))),var(--glass-wash);min-height:52px;align-content:center;box-shadow:inset 0 1px 0 color-mix(in srgb,var(--crystal-rim) 68%,transparent),0 8px 18px rgba(0,0,0,.18);min-width:0;backdrop-filter:blur(10px) saturate(0.92)}
@@ -767,6 +769,8 @@ button.operations-inline-warning{cursor:pointer}
.inline-link-command{border:0;padding:0;background:0 0;color:var(--accent-deep);text-decoration:underline;cursor:pointer} .inline-link-command{border:0;padding:0;background:0 0;color:var(--accent-deep);text-decoration:underline;cursor:pointer}
.inline-link-command:focus-visible,.inline-link-command:hover{color:var(--ink);outline:0} .inline-link-command:focus-visible,.inline-link-command:hover{color:var(--ink);outline:0}
.server-card-warning{justify-self:start;color:var(--gold)} .server-card-warning{justify-self:start;color:var(--gold)}
.server-card .action-strip{min-width:0}
.server-card .action-strip button{min-width:0;white-space:nowrap}
.resource-filter-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:12px 0} .resource-filter-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:12px 0}
.resource-filter-bar input,.resource-filter-bar select{min-height:36px;min-width:150px;border:1px solid var(--line-strong);border-radius:6px;padding:0 10px;background:var(--surface-solid);color:var(--ink);font:inherit} .resource-filter-bar input,.resource-filter-bar select{min-height:36px;min-width:150px;border:1px solid var(--line-strong);border-radius:6px;padding:0 10px;background:var(--surface-solid);color:var(--ink);font:inherit}
.resource-filter-bar input{flex:1 1 240px} .resource-filter-bar input{flex:1 1 240px}
+14
View File
@@ -0,0 +1,14 @@
import { describe, expect, it } from "vitest";
import { formatTerminalLogTime } from "./logTime";
describe("formatTerminalLogTime", () => {
it("uses the SCUM log clock instead of the browser timezone", () => {
expect(formatTerminalLogTime("2026-08-24T01:23:44Z", "[2026.08.23-17.23.44][418]LogSCUM: Global Stats"))
.toBe("17:23:44");
});
it("falls back to the event timestamp for generic logs", () => {
expect(formatTerminalLogTime("not-a-timestamp")).toBe("时间未知");
});
});
+11
View File
@@ -0,0 +1,11 @@
const scumLogTimestampPattern = /\[?(\d{4})\.(\d{2})\.(\d{2})-(\d{2})\.(\d{2})\.(\d{2})(?:[.:]\d{1,3})?\]?/;
export function formatTerminalLogTime(timestamp: string, line?: string): string {
const embedded = line?.match(scumLogTimestampPattern);
if (embedded) {
return `${embedded[4]}:${embedded[5]}:${embedded[6]}`;
}
const date = new Date(timestamp);
return Number.isNaN(date.getTime()) ? "时间未知" : date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
}