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 1/9] 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; From 2a1388a5a29b7fee0134acf620c052c5c46f3ce4 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:54:36 +0100 Subject: [PATCH 2/9] Remove uneeded style --- InfoLogger/public/log/statusBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InfoLogger/public/log/statusBar.js b/InfoLogger/public/log/statusBar.js index 64a24e983..ebe89b8e4 100644 --- a/InfoLogger/public/log/statusBar.js +++ b/InfoLogger/public/log/statusBar.js @@ -26,7 +26,7 @@ export default (model) => [ model.log.isActiveModeQuery() && h('.flex-grow', { style: 'text-align: center; width:30%' }, sqlQuery(model)), h( '.flex-row.flex-grow.items-center.gc3', - { id: 'status-bar-application-options', style: 'justify-content: flex-end;' }, + { id: 'status-bar-application-options' }, applicationMessage(model), applicationOptions(model), ), From ee65414b450b4115413f3c807bb845458cc30571 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:54:50 +0100 Subject: [PATCH 3/9] Add comment explaining CSS class --- InfoLogger/public/app.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/InfoLogger/public/app.css b/InfoLogger/public/app.css index 7f5a67561..580d0fffc 100644 --- a/InfoLogger/public/app.css +++ b/InfoLogger/public/app.css @@ -269,6 +269,9 @@ a.disabled { pointer-events: none; cursor: default; } font-variant-numeric: tabular-nums; } +/* ensure status bar has a minimum height +otherwise line-height of text at 1.5em and the height of autoscroll button at 1.6 em might causes height change between +live mode running and it not. */ #status-bar { min-height: 1.6em; } From 35b21cada2e4d12e6ac0079fc6d927ea133d6ac4 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:00:41 +0100 Subject: [PATCH 4/9] Size the autoscroll button to the text line height instead of the other way around --- InfoLogger/public/app.css | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/InfoLogger/public/app.css b/InfoLogger/public/app.css index 580d0fffc..0e043522e 100644 --- a/InfoLogger/public/app.css +++ b/InfoLogger/public/app.css @@ -269,11 +269,10 @@ a.disabled { pointer-events: none; cursor: default; } font-variant-numeric: tabular-nums; } -/* ensure status bar has a minimum height -otherwise line-height of text at 1.5em and the height of autoscroll button at 1.6 em might causes height change between -live mode running and it not. */ -#status-bar { - min-height: 1.6em; +/* match the 1.5em text line height so the status bar does not change height when the button appears in a running live mode */ +#status-bar-auto-scroll { + padding-block: 0; + line-height: 1.5; } #status-bar-application-options { From b8221a765405b94f6ec8a196b13891825e3c8b8a Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:24:57 +0100 Subject: [PATCH 5/9] Keep fixed width for the status bar options --- InfoLogger/public/log/statusBar.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/InfoLogger/public/log/statusBar.js b/InfoLogger/public/log/statusBar.js index ebe89b8e4..0f4aeeeb0 100644 --- a/InfoLogger/public/log/statusBar.js +++ b/InfoLogger/public/log/statusBar.js @@ -22,11 +22,11 @@ import { h, iconWarning } from '/js/src/index.js'; */ export default (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.flex-wrap.items-center.gc2', statusLogs(model)), + model.log.isActiveModeQuery() && h('', { style: 'text-align: center; width:30%' }, sqlQuery(model)), h( - '.flex-row.flex-grow.items-center.gc3', - { id: 'status-bar-application-options' }, + '.flex-row.items-center.gc3', + { id: 'status-bar-application-options', style: 'width:20%' }, applicationMessage(model), applicationOptions(model), ), From 491123cc92da56df5e117e5d48a477a0c731ea25 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:25:13 +0100 Subject: [PATCH 6/9] Use tabular digits for the live duration --- InfoLogger/public/log/statusBar.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/InfoLogger/public/log/statusBar.js b/InfoLogger/public/log/statusBar.js index 0f4aeeeb0..0dd08267b 100644 --- a/InfoLogger/public/log/statusBar.js +++ b/InfoLogger/public/log/statusBar.js @@ -139,7 +139,11 @@ const statusQuery = (model, result) => `in ${(result.time / 1000).toFixed(2)}s`; * @returns {vnode} - the view of the live status */ const statusLive = (model, frameworkInfo) => - [`(Connected to ${frameworkInfo.mysql.host} for ${model.timezone.formatDuration(model.log.liveStartedAt)})`]; + h('span', [ + `(Connected to ${frameworkInfo.mysql.host} for `, + h('span.status-number', model.timezone.formatDuration(model.log.liveStartedAt)), + ')', + ]); /** * Status of the log's list (independent of mode): how many global and how many per severity From d80cd3f3d7bc96ccc92382ea8060a64871165b25 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:25:29 +0100 Subject: [PATCH 7/9] Remove extra margin --- InfoLogger/public/app.css | 1 + 1 file changed, 1 insertion(+) diff --git a/InfoLogger/public/app.css b/InfoLogger/public/app.css index 0e043522e..55d2a9453 100644 --- a/InfoLogger/public/app.css +++ b/InfoLogger/public/app.css @@ -273,6 +273,7 @@ a.disabled { pointer-events: none; cursor: default; } #status-bar-auto-scroll { padding-block: 0; line-height: 1.5; + margin-left: 0; /* .btn-sm adds its own margin; spacing comes from the parent's gap */ } #status-bar-application-options { From db0008929c601393960b93302c1039ebd1205348 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:25:39 +0100 Subject: [PATCH 8/9] Remove whitespace --- InfoLogger/public/app.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InfoLogger/public/app.css b/InfoLogger/public/app.css index 55d2a9453..afa1752a8 100644 --- a/InfoLogger/public/app.css +++ b/InfoLogger/public/app.css @@ -276,8 +276,8 @@ a.disabled { pointer-events: none; cursor: default; } margin-left: 0; /* .btn-sm adds its own margin; spacing comes from the parent's gap */ } -#status-bar-application-options { - justify-content: flex-end; +#status-bar-application-options { + justify-content: flex-end; } #status-bar-buffer-dot { From e2181115f67f4b814c155d7bbbd832e5f3a06180 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:35:55 +0100 Subject: [PATCH 9/9] Show the limit warning in the stats column so it doesn't get wrapped --- InfoLogger/public/log/statusBar.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/InfoLogger/public/log/statusBar.js b/InfoLogger/public/log/statusBar.js index 0dd08267b..cc5d65d07 100644 --- a/InfoLogger/public/log/statusBar.js +++ b/InfoLogger/public/log/statusBar.js @@ -22,12 +22,11 @@ import { h, iconWarning } from '/js/src/index.js'; */ export default (model) => [ h('.flex-row.items-center.ph2.mv2.gc3', { id: 'status-bar' }, [ - h('.flex-grow.flex-wrap.items-center.gc2', statusLogs(model)), + h('.flex-grow.flex-wrap.items-center.gc2', [statusLogs(model), applicationMessage(model)]), model.log.isActiveModeQuery() && h('', { style: 'text-align: center; width:30%' }, sqlQuery(model)), h( '.flex-row.items-center.gc3', { id: 'status-bar-application-options', style: 'width:20%' }, - applicationMessage(model), applicationOptions(model), ), ]), @@ -85,7 +84,7 @@ const applicationMessage = (model) => model.log.list.length > model.log.applicat ? h( 'span.danger', { id: 'status-bar-application-message' }, - `Application reached more than ${model.log.applicationLimit} logs, please clear if possible `, + `Application reached more than ${model.log.applicationLimit} logs, please clear if possible`, ) : null;