From b09d1ad90bb88283a55d99f56205c51239b32a38 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Fri, 23 Oct 2026 18:35:29 +0200 Subject: [PATCH] Stabilize InfoLogger status bar layout Prevent the status bar from shifting when counters change. The layout is converted to flex utilities with consistent gaps instead of ad-hoc padding and margins. The SQL query block is shown only when the query mode is active. --- InfoLogger/public/app.css | 16 +++++++ InfoLogger/public/log/Log.js | 1 + InfoLogger/public/log/statusBar.js | 54 +++++++++++++++------- InfoLogger/public/view.js | 2 +- InfoLogger/test/public/status-bar-mocha.js | 4 +- 5 files changed, 57 insertions(+), 20 deletions(-) diff --git a/InfoLogger/public/app.css b/InfoLogger/public/app.css index b3decfe96..7f5a67561 100644 --- a/InfoLogger/public/app.css +++ b/InfoLogger/public/app.css @@ -264,3 +264,19 @@ a.disabled { pointer-events: none; cursor: default; } flex-direction: column; gap: 0.25rem; } + +.status-number { + font-variant-numeric: tabular-nums; +} + +#status-bar { + min-height: 1.6em; +} + +#status-bar-application-options { + justify-content: flex-end; +} + +#status-bar-buffer-dot { + margin-right: var(--space-xs); +} diff --git a/InfoLogger/public/log/Log.js b/InfoLogger/public/log/Log.js index 3ac07791d..884773926 100644 --- a/InfoLogger/public/log/Log.js +++ b/InfoLogger/public/log/Log.js @@ -490,6 +490,7 @@ export default class Log extends Observable { this.limitReached = null; this.resetStats(); this.queryResult = RemoteData.notAsked(); // empty all data from last query + this.statusDropdown = false; this.activeMode = MODE.LIVE.RUNNING; this.liveStartedAt = new Date(); diff --git a/InfoLogger/public/log/statusBar.js b/InfoLogger/public/log/statusBar.js index 526dca273..64a24e983 100644 --- a/InfoLogger/public/log/statusBar.js +++ b/InfoLogger/public/log/statusBar.js @@ -16,17 +16,17 @@ import { h, iconWarning } from '/js/src/index.js'; /** * Bottom bar, showing status of the log's list and its details, - * some application messages and some basic options like auto-scroll label/button. + * some application messages and some basic options like auto-scroll button. * @param {Model} model - root model of the application * @returns {vnode} - the view of the bottom bar */ export default (model) => [ - h('.flex-row', { id: 'status-bar' }, [ - h('', { style: 'width:50%' }, statusLogs(model)), - h('', { style: 'text-align: center; width:30%' }, sqlQuery(model)), + h('.flex-row.items-center.ph2.mv2.gc3', { id: 'status-bar' }, [ + h('.flex-grow.flex-wrap.items-center.gc2', { style: 'width:50%' }, statusLogs(model)), + model.log.isActiveModeQuery() && h('.flex-grow', { style: 'text-align: center; width:30%' }, sqlQuery(model)), h( - '.flex-grow.text-right', - { id: 'status-bar-application-options' }, + '.flex-row.flex-grow.items-center.gc3', + { id: 'status-bar-application-options', style: 'justify-content: flex-end;' }, applicationMessage(model), applicationOptions(model), ), @@ -115,13 +115,12 @@ const applicationOptions = (model) => { h('span.d-inline', autoScrollEnabled ? 'Autoscroll Enabled' : 'Autoscroll Disabled'), ], ), - h('span.mh1'), ], - h('label.d-inline', { title: 'Show details of selected log' }, h('input', { + h('label.flex-row.items-center.g1.m0', { title: 'Show details of selected log' }, h('input', { type: 'checkbox', checked: model.inspectorEnabled, onchange: () => model.toggleInspector(), - }), ' Inspector'), + }), 'Inspector'), ]; }; @@ -149,13 +148,16 @@ const statusLive = (model, frameworkInfo) => */ const statusStats = (model) => [ h( - 'span.ph1', + 'span', { id: 'status-bar-buffer-size', }, [ bufferStatus(model), - `${model.log.list.length.toLocaleString('en-US')} / ${model.log.limit.toLocaleString('en-US')} (Buffer size)`, + statNumber(model.log.list.length), + ' / ', + statNumber(model.log.limit), + ' (Buffer size)', ], ), model.log.queryResult.match({ @@ -164,13 +166,31 @@ const statusStats = (model) => [ Success: (result) => statusQuery(model, result), Failure: () => null, // notification }), - h('span.ph1.severity-d', `${model.log.stats.debug} debug`), - h('span.ph1.severity-i', `${model.log.stats.info} info`), - h('span.ph1.severity-w', `${model.log.stats.warning} warn`), - h('span.ph1.severity-e', `${model.log.stats.error} error`), - h('span.ph1.severity-f', `${model.log.stats.fatal} fatal`), + statLabel(model.log.stats.debug, 'debug'), + statLabel(model.log.stats.info, 'info'), + statLabel(model.log.stats.warning, 'warn'), + statLabel(model.log.stats.error, 'error'), + statLabel(model.log.stats.fatal, 'fatal'), ]; +/** + * Number to display in the status bar in a locale aware format + * Digits use tabular figures so a count changing without gaining a digit does not shift the status bar + * @param {number} value - number to display + * @returns {vnode} - view of the number + */ +const statNumber = (value) => + h('span.status-number', value.toLocaleString('en-US')); + +/** + * Label for a specific severity level in the status bar + * @param {number} value - number of logs for this severity + * @param {string} severity - name of the severity level + * @returns {vnode} - view of the severity label + */ +const statLabel = (value, severity) => + h(`span.severity-${severity.charAt(0)}`, [statNumber(value), ` ${severity}`]); + const bufferStatus = (model) => { let dotClass = 'gray-darker'; // grey - unknown status default @@ -180,7 +200,7 @@ const bufferStatus = (model) => { dotClass = 'success'; // green - limit not reached } - return h(`span.${dotClass}.f7.mh1`, { + return h(`span.${dotClass}.f7`, { title: model.log.limitReached === null ? 'No query data loaded' : model.log.limitReached === true ? 'Limit reached - results may be incomplete' : 'Limit OK', id: 'status-bar-buffer-dot', diff --git a/InfoLogger/public/view.js b/InfoLogger/public/view.js index 862f75c12..089980cad 100644 --- a/InfoLogger/public/view.js +++ b/InfoLogger/public/view.js @@ -75,7 +75,7 @@ export default (model) => [ logsTable(model), inspectorSide(model), ]), - h('footer.f7.ph1', [statusBar(model)]), + h('footer.f7', [statusBar(model)]), ], ), ]; diff --git a/InfoLogger/test/public/status-bar-mocha.js b/InfoLogger/test/public/status-bar-mocha.js index bc11f6756..0a40b8c85 100644 --- a/InfoLogger/test/public/status-bar-mocha.js +++ b/InfoLogger/test/public/status-bar-mocha.js @@ -26,8 +26,8 @@ async function getSeverityCounts(page) { const counts = {}; const severityLabels = ['d', 'i', 'w', 'e', 'f']; severityLabels.forEach((label) => { - const el = document.querySelector(`span.ph1.severity-${label}`); - const count = Number(el.textContent.trim().split(' ')[0]); + const el = document.querySelector(`span.severity-${label} .status-number`); + const count = Number(el.textContent.replace(/,/g, '')); counts[label.toUpperCase()] = count; }); return counts;