leaderboard-raderna i overlay är tätare igen
This commit is contained in:
30
src/app.js
30
src/app.js
@@ -1083,6 +1083,7 @@ let selectedTeamEditId = null;
|
|||||||
let quickAddDraft = null;
|
let quickAddDraft = null;
|
||||||
let overlaySyncTimer = null;
|
let overlaySyncTimer = null;
|
||||||
let overlayRotationTimer = null;
|
let overlayRotationTimer = null;
|
||||||
|
let overlayLiveRefreshTimer = null;
|
||||||
let overlayRotationIndex = 0;
|
let overlayRotationIndex = 0;
|
||||||
let overlayEvents = [];
|
let overlayEvents = [];
|
||||||
let lastOverlayLeaderKeyBySession = {};
|
let lastOverlayLeaderKeyBySession = {};
|
||||||
@@ -1131,6 +1132,7 @@ async function init() {
|
|||||||
if (overlayMode) {
|
if (overlayMode) {
|
||||||
startOverlaySync();
|
startOverlaySync();
|
||||||
startOverlayRotation();
|
startOverlayRotation();
|
||||||
|
startOverlayLiveRefresh();
|
||||||
if (state.settings.wsUrl) {
|
if (state.settings.wsUrl) {
|
||||||
connectDecoder();
|
connectDecoder();
|
||||||
}
|
}
|
||||||
@@ -1704,6 +1706,15 @@ function startOverlayRotation() {
|
|||||||
}, 8000);
|
}, 8000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startOverlayLiveRefresh() {
|
||||||
|
clearInterval(overlayLiveRefreshTimer);
|
||||||
|
overlayLiveRefreshTimer = setInterval(() => {
|
||||||
|
if (currentView === "overlay" && ["leaderboard", "tv", "team"].includes(overlayViewMode)) {
|
||||||
|
renderOverlay();
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
|
||||||
function renderNav() {
|
function renderNav() {
|
||||||
if (overlayMode) {
|
if (overlayMode) {
|
||||||
dom.nav.innerHTML = "";
|
dom.nav.innerHTML = "";
|
||||||
@@ -5679,16 +5690,20 @@ function buildLeaderboard(session) {
|
|||||||
const distanceToTargetMs = Math.abs(targetMs - totalElapsedMs);
|
const distanceToTargetMs = Math.abs(targetMs - totalElapsedMs);
|
||||||
const seedMetric = getCompetitorSeedMetric(session, row);
|
const seedMetric = getCompetitorSeedMetric(session, row);
|
||||||
const passings = getCompetitorPassings(session, row);
|
const passings = getCompetitorPassings(session, row);
|
||||||
|
const latestPassing = passings.length ? passings[passings.length - 1] : null;
|
||||||
const previousLapMs = passings.length >= 2 ? Number(passings[passings.length - 2].lapMs || 0) : null;
|
const previousLapMs = passings.length >= 2 ? Number(passings[passings.length - 2].lapMs || 0) : null;
|
||||||
|
const lastLapMs = latestPassing ? Number(latestPassing.lapMs || 0) : Number(row.lastLapMs || 0) || 0;
|
||||||
|
const bestLapMs = Number(row.bestLapMs || 0) || 0;
|
||||||
|
const lastPassingTs = latestPassing ? Number(latestPassing.timestamp || 0) : Number(row.lastTimestamp || 0) || 0;
|
||||||
const lapDeltaMs =
|
const lapDeltaMs =
|
||||||
row.lastLapMs && previousLapMs && row.lastLapMs > 0 && previousLapMs > 0 ? row.lastLapMs - previousLapMs : null;
|
lastLapMs && previousLapMs && lastLapMs > 0 && previousLapMs > 0 ? lastLapMs - previousLapMs : null;
|
||||||
const predictionBaseMs =
|
const predictionBaseMs =
|
||||||
Number(row.lastLapMs || 0) > 0
|
lastLapMs > 0
|
||||||
? Number(row.lastLapMs)
|
? lastLapMs
|
||||||
: Number(row.bestLapMs || 0) > 0
|
: bestLapMs > 0
|
||||||
? Number(row.bestLapMs)
|
? bestLapMs
|
||||||
: null;
|
: null;
|
||||||
const currentLapElapsedMs = row.lastTimestamp ? Math.max(0, nowTs - row.lastTimestamp) : 0;
|
const currentLapElapsedMs = lastPassingTs ? Math.max(0, nowTs - lastPassingTs) : 0;
|
||||||
const predictedRemainingMs = predictionBaseMs ? Math.max(0, predictionBaseMs - currentLapElapsedMs) : null;
|
const predictedRemainingMs = predictionBaseMs ? Math.max(0, predictionBaseMs - currentLapElapsedMs) : null;
|
||||||
const predictedProgress = predictionBaseMs ? Math.min(1.25, currentLapElapsedMs / predictionBaseMs) : 0;
|
const predictedProgress = predictionBaseMs ? Math.min(1.25, currentLapElapsedMs / predictionBaseMs) : 0;
|
||||||
const predictionTone =
|
const predictionTone =
|
||||||
@@ -5699,6 +5714,9 @@ function buildLeaderboard(session) {
|
|||||||
: "late";
|
: "late";
|
||||||
return {
|
return {
|
||||||
...row,
|
...row,
|
||||||
|
lastLapMs,
|
||||||
|
bestLapMs,
|
||||||
|
lastTimestamp: lastPassingTs || row.lastTimestamp,
|
||||||
totalElapsedMs,
|
totalElapsedMs,
|
||||||
distanceToTargetMs,
|
distanceToTargetMs,
|
||||||
seedMetric,
|
seedMetric,
|
||||||
|
|||||||
110
src/styles.css
110
src/styles.css
@@ -780,7 +780,7 @@ select:focus {
|
|||||||
|
|
||||||
.overlay-shell {
|
.overlay-shell {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 14px;
|
padding: 10px;
|
||||||
background:
|
background:
|
||||||
radial-gradient(circle at 15% 0%, rgba(225, 6, 0, 0.18), transparent 30%),
|
radial-gradient(circle at 15% 0%, rgba(225, 6, 0, 0.18), transparent 30%),
|
||||||
radial-gradient(circle at 100% 80%, rgba(37, 59, 103, 0.22), transparent 30%),
|
radial-gradient(circle at 100% 80%, rgba(37, 59, 103, 0.22), transparent 30%),
|
||||||
@@ -790,15 +790,15 @@ select:focus {
|
|||||||
.overlay-header {
|
.overlay-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-header-main {
|
.overlay-header-main {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,15 +809,15 @@ select:focus {
|
|||||||
.overlay-kicker-row {
|
.overlay-kicker-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 4px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-header h1 {
|
.overlay-header h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: Orbitron, sans-serif;
|
font-family: Orbitron, sans-serif;
|
||||||
font-size: clamp(1.05rem, 1.7vw, 1.6rem);
|
font-size: clamp(0.92rem, 1.35vw, 1.25rem);
|
||||||
line-height: 1.02;
|
line-height: 1.02;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -826,13 +826,13 @@ select:focus {
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
font-size: 0.62rem;
|
font-size: 0.56rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-header-sub {
|
.overlay-header-sub {
|
||||||
margin: 3px 0 0;
|
margin: 1px 0 0;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 0.74rem;
|
font-size: 0.66rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-meta {
|
.overlay-meta {
|
||||||
@@ -844,7 +844,7 @@ select:focus {
|
|||||||
|
|
||||||
.overlay-clock {
|
.overlay-clock {
|
||||||
font-family: Orbitron, sans-serif;
|
font-family: Orbitron, sans-serif;
|
||||||
font-size: clamp(1.35rem, 2.3vw, 2.2rem);
|
font-size: clamp(1.12rem, 1.9vw, 1.7rem);
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,13 +856,13 @@ select:focus {
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.08em;
|
letter-spacing: 0.08em;
|
||||||
font-size: 0.62rem;
|
font-size: 0.56rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-board {
|
.overlay-board {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(0, 1.85fr) minmax(210px, 0.38fr);
|
grid-template-columns: minmax(0, 1.92fr) minmax(180px, 0.31fr);
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-board-tv {
|
.overlay-board-tv {
|
||||||
@@ -964,12 +964,12 @@ select:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.overlay-table-wrap {
|
.overlay-table-wrap {
|
||||||
padding: 6px 8px 8px;
|
padding: 4px 6px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-display-wrap {
|
.overlay-display-wrap {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-fastest-banner,
|
.overlay-fastest-banner,
|
||||||
@@ -984,8 +984,8 @@ select:focus {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: end;
|
align-items: end;
|
||||||
gap: 12px;
|
gap: 8px;
|
||||||
padding: 10px 12px;
|
padding: 7px 10px;
|
||||||
background:
|
background:
|
||||||
linear-gradient(135deg, rgba(225, 6, 0, 0.18), rgba(225, 6, 0, 0.04)),
|
linear-gradient(135deg, rgba(225, 6, 0, 0.18), rgba(225, 6, 0, 0.04)),
|
||||||
rgba(7, 12, 20, 0.92);
|
rgba(7, 12, 20, 0.92);
|
||||||
@@ -1002,20 +1002,20 @@ select:focus {
|
|||||||
display: block;
|
display: block;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
font-family: Orbitron, sans-serif;
|
font-family: Orbitron, sans-serif;
|
||||||
font-size: clamp(1.25rem, 2.3vw, 1.9rem);
|
font-size: clamp(1.02rem, 1.7vw, 1.45rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-leaderboard-card {
|
.overlay-leaderboard-card {
|
||||||
padding: 8px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-leaderboard-card-tv {
|
.overlay-leaderboard-card-tv {
|
||||||
min-height: calc(100vh - 152px);
|
min-height: calc(100vh - 118px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-shell-dense .overlay-logo {
|
.overlay-shell-dense .overlay-logo {
|
||||||
width: 44px;
|
width: 34px;
|
||||||
height: 44px;
|
height: 34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-fastest-banner-dense {
|
.overlay-fastest-banner-dense {
|
||||||
@@ -1027,13 +1027,13 @@ select:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.overlay-fastest-driver {
|
.overlay-fastest-driver {
|
||||||
font-size: 0.9rem;
|
font-size: 0.76rem;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-fastest-meta {
|
.overlay-fastest-meta {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 0.66rem;
|
font-size: 0.58rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.06em;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -1045,24 +1045,24 @@ select:focus {
|
|||||||
|
|
||||||
.overlay-race-metric strong,
|
.overlay-race-metric strong,
|
||||||
.overlay-race-best strong {
|
.overlay-race-best strong {
|
||||||
font-size: clamp(0.88rem, 1.15vw, 1.02rem);
|
font-size: clamp(0.78rem, 0.98vw, 0.92rem);
|
||||||
line-height: 1.05;
|
line-height: 1.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-race-pos .pos-pill {
|
.overlay-race-pos .pos-pill {
|
||||||
min-width: 32px;
|
min-width: 26px;
|
||||||
height: 32px;
|
height: 26px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.76rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-shell-dense .pill {
|
.overlay-shell-dense .pill {
|
||||||
padding: 4px 8px;
|
padding: 3px 6px;
|
||||||
font-size: 0.62rem;
|
font-size: 0.54rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-shell-dense .btn.overlay-fullscreen-btn {
|
.overlay-shell-dense .btn.overlay-fullscreen-btn {
|
||||||
padding: 8px 10px;
|
padding: 6px 8px;
|
||||||
font-size: 0.72rem;
|
font-size: 0.66rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-section-head {
|
.overlay-section-head {
|
||||||
@@ -1105,28 +1105,28 @@ select:focus {
|
|||||||
|
|
||||||
.overlay-side {
|
.overlay-side {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
font-size: 0.84rem;
|
font-size: 0.78rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-side-card {
|
.overlay-side-card {
|
||||||
padding: 8px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-rotating-card {
|
.overlay-rotating-card {
|
||||||
min-height: 220px;
|
min-height: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-side-card h3 {
|
.overlay-side-card h3 {
|
||||||
margin: 0 0 4px;
|
margin: 0 0 3px;
|
||||||
font-size: 0.86rem;
|
font-size: 0.78rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-passing {
|
.overlay-passing {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 6px;
|
gap: 5px;
|
||||||
padding: 4px 0;
|
padding: 3px 0;
|
||||||
border-bottom: 1px solid var(--line);
|
border-bottom: 1px solid var(--line);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1135,35 +1135,35 @@ select:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.overlay-side-card .overlay-passing strong {
|
.overlay-side-card .overlay-passing strong {
|
||||||
font-size: 0.8rem;
|
font-size: 0.72rem;
|
||||||
line-height: 1.15;
|
line-height: 1.15;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-side-card .overlay-passing span {
|
.overlay-side-card .overlay-passing span {
|
||||||
font-family: Orbitron, sans-serif;
|
font-family: Orbitron, sans-serif;
|
||||||
font-size: 0.72rem;
|
font-size: 0.64rem;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-side-card .pill {
|
.overlay-side-card .pill {
|
||||||
padding: 3px 7px;
|
padding: 3px 7px;
|
||||||
font-size: 0.58rem;
|
font-size: 0.54rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-race-list {
|
.overlay-race-list {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 6px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-race-row {
|
.overlay-race-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 48px minmax(200px, 1.55fr) repeat(3, minmax(102px, 0.7fr)) minmax(110px, 0.72fr);
|
grid-template-columns: 38px minmax(170px, 1.72fr) repeat(3, minmax(90px, 0.62fr)) minmax(95px, 0.66fr);
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 10px;
|
padding: 5px 7px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
border-radius: 10px;
|
border-radius: 8px;
|
||||||
background: rgba(255, 255, 255, 0.03);
|
background: rgba(255, 255, 255, 0.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1179,7 +1179,7 @@ select:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.overlay-race-driver strong {
|
.overlay-race-driver strong {
|
||||||
font-size: clamp(0.98rem, 1.35vw, 1.18rem);
|
font-size: clamp(0.82rem, 1.04vw, 0.96rem);
|
||||||
line-height: 1.05;
|
line-height: 1.05;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1187,32 +1187,32 @@ select:focus {
|
|||||||
.overlay-race-metric label,
|
.overlay-race-metric label,
|
||||||
.overlay-race-best label {
|
.overlay-race-best label {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 0.62rem;
|
font-size: 0.54rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.06em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-prediction {
|
.overlay-prediction {
|
||||||
margin-top: 4px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-prediction-meta {
|
.overlay-prediction-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-bottom: 3px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-prediction-meta label,
|
.overlay-prediction-meta label,
|
||||||
.overlay-prediction-meta span {
|
.overlay-prediction-meta span {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 0.58rem;
|
font-size: 0.5rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.06em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay-prediction-track {
|
.overlay-prediction-track {
|
||||||
height: 3px;
|
height: 2px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: rgba(255, 255, 255, 0.08);
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
|||||||
Reference in New Issue
Block a user