From 75b7527f73e2386108631ac28ed9ca1efbc27145 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Fri, 9 Oct 2026 13:00:26 +0200 Subject: [PATCH 1/7] Revamp autoscroll status control Replace the autoscroll checkbox with a clearer UI. Show a success label when enabled and a warning button when disabled, which when clicked re-enables it. --- InfoLogger/public/log/Log.js | 7 +++---- InfoLogger/public/log/statusBar.js | 26 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/InfoLogger/public/log/Log.js b/InfoLogger/public/log/Log.js index 03608b52c..db9af7368 100644 --- a/InfoLogger/public/log/Log.js +++ b/InfoLogger/public/log/Log.js @@ -575,11 +575,10 @@ export default class Log extends Observable { } /** - * Enable or disable auto-scroll for live mode, a checkbox is used to control it + * Enable auto-scroll for live mode and scroll to the bottom of the table */ - toggleAutoScroll() { - this.autoScrollLive = !this.autoScrollLive; - this.dom.table.focus(); + enableAutoScrollLiveAndNotify() { + this.autoScrollLive = true; this.notify(); } diff --git a/InfoLogger/public/log/statusBar.js b/InfoLogger/public/log/statusBar.js index 3d99f4198..bbcfa3d26 100644 --- a/InfoLogger/public/log/statusBar.js +++ b/InfoLogger/public/log/statusBar.js @@ -12,11 +12,14 @@ * or submit itself to any jurisdiction. */ -import { h } from '/js/src/index.js'; +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 checkbox. + * some application messages and some basic options like auto-scroll label/button. * @param {Model} model - root model of the application * @returns {vnode} - the view of the bottom bar */ @@ -97,11 +100,20 @@ const applicationMessage = (model) => model.log.list.length > model.log.applicat */ const applicationOptions = (model) => [ model.log.isLiveModeRunning() && [ - h('label.d-inline', { title: 'Scroll down in live mode on new log incoming' }, h('input', { - type: 'checkbox', - checked: model.log.autoScrollLive, - onchange: () => model.log.toggleAutoScroll(), - }), ' Autoscroll'), + model.log.autoScrollLive + ? h('span.success', { id: 'status-bar-auto-scroll', title: 'Autoscroll is enabled' }, 'Autoscroll Enabled') + : h( + 'button.btn.btn-sm.btn-warning', + { + id: 'status-bar-auto-scroll', + title: 'Click here to re-enable autoscroll', + onclick: () => model.log.enableAutoScrollLiveAndNotify(), + }, + [ + iconWarning(), + h('span.d-inline', 'Autoscroll Disabled'), + ], + ), h('span.mh1'), ], h('label.d-inline', { title: 'Show details of selected log' }, h('input', { From 0356856e89443c8af67bf93d4afea37d7e071a45 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Fri, 9 Oct 2026 13:01:28 +0200 Subject: [PATCH 2/7] Reset last autoScroll log when autoScroll is disabled Delete the table's last scrolled log marker when autoscroll is off so re-enabling immediately jumps to the newest logs. --- InfoLogger/public/log/tableLogsContent.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/InfoLogger/public/log/tableLogsContent.js b/InfoLogger/public/log/tableLogsContent.js index 2250078ca..b72db7a20 100644 --- a/InfoLogger/public/log/tableLogsContent.js +++ b/InfoLogger/public/log/tableLogsContent.js @@ -270,6 +270,9 @@ const autoscrollManager = (model, vnode) => { return; } + // forget the last scrolled log so re-enabling autoscroll jumps to bottom straight away on a redraw + delete vnode.dom.dataset.lastLogId; + // Autoscroll to selected item if (model.log.item) { // Scroll only if we did not previously, save last try in DOM dataset From 1ad3ec7bd479972715c1d7421a945034c804feef Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Fri, 9 Oct 2026 13:02:03 +0200 Subject: [PATCH 3/7] Update status-bar-mocha.js Update and add status bar tests to cover the new enabled/disabled states and re-enable behaviour. --- InfoLogger/test/public/status-bar-mocha.js | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/InfoLogger/test/public/status-bar-mocha.js b/InfoLogger/test/public/status-bar-mocha.js index 4080ae337..bbaf9ad4a 100644 --- a/InfoLogger/test/public/status-bar-mocha.js +++ b/InfoLogger/test/public/status-bar-mocha.js @@ -35,7 +35,7 @@ async function getSeverityCounts(page) { } describe('Status Bar test-suite', async () => { - const AUTOSCROLL_SELECTOR = '#status-bar-application-options label[title*="Scroll down"] input'; + const AUTOSCROLL_SELECTOR = '#status-bar-auto-scroll'; const INSPECTOR_SELECTOR = '#status-bar-application-options label[title*="Show details"] input'; const SQL_QUERY = 'SELECT * FROM `messages` WHERE `timestamp`>=? AND `severity` IN (?) ' + 'ORDER BY `TIMESTAMP` LIMIT 100000'; @@ -357,16 +357,23 @@ describe('Status Bar test-suite', async () => { await page.waitForSelector(AUTOSCROLL_SELECTOR, { timeout: 2000 }); }); - it('should toggle autoscroll when its checkbox is clicked', async () => { - await page.waitForSelector(AUTOSCROLL_SELECTOR, { timeout: 2000 }); - const checkedBefore = await page.$eval(AUTOSCROLL_SELECTOR, (el) => el.checked); - await page.click(AUTOSCROLL_SELECTOR); + it('should show correct label when autoscroll is enabled', async () => { + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Enabled'); + }); - const checkedAfter = await page.$eval(AUTOSCROLL_SELECTOR, (el) => el.checked); - const autoScrollModelValue = await page.evaluate(() => model.log.autoScrollLive); + it('should show correct button when autoscroll is disabled', async () => { + await page.evaluate(() => model.log.setAutoScrollLive(false)); + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); + }); + + it('should re-enable autoscroll when disabled autoscroll button is clicked', async () => { + await page.evaluate(() => model.log.setAutoScrollLive(false)); + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); + + await page.click(AUTOSCROLL_SELECTOR); - assert.strictEqual(checkedAfter, !checkedBefore); - assert.strictEqual(autoScrollModelValue, checkedAfter); + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Enabled'); + assert.strictEqual(await page.evaluate(() => model.log.autoScrollLive), true); }); }); }); From d8aab5baf12367e5920764d3fbbb6c3efcdb2a1c Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Fri, 9 Oct 2026 14:37:05 +0200 Subject: [PATCH 4/7] Fix status bar autoscroll test --- InfoLogger/test/public/status-bar-mocha.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/InfoLogger/test/public/status-bar-mocha.js b/InfoLogger/test/public/status-bar-mocha.js index bbaf9ad4a..5335c6638 100644 --- a/InfoLogger/test/public/status-bar-mocha.js +++ b/InfoLogger/test/public/status-bar-mocha.js @@ -362,12 +362,16 @@ describe('Status Bar test-suite', async () => { }); it('should show correct button when autoscroll is disabled', async () => { - await page.evaluate(() => model.log.setAutoScrollLive(false)); + await page.evaluate(() => { + model.log.autoScrollLive = false; + }); await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); }); it('should re-enable autoscroll when disabled autoscroll button is clicked', async () => { - await page.evaluate(() => model.log.setAutoScrollLive(false)); + await page.evaluate(() => { + model.log.autoScrollLive = false; + }); await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); await page.click(AUTOSCROLL_SELECTOR); From 3014eb50e7bc060b3646e9d7f5ef140aef3771fc Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Fri, 9 Oct 2026 16:24:25 +0200 Subject: [PATCH 5/7] Keep status bar height stable when autoscroll toggles Plain span is 1.5em. .btn-sm is 1.6em. Give both states the same div box sized to 1.5em. --- InfoLogger/public/app.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/InfoLogger/public/app.css b/InfoLogger/public/app.css index b3decfe96..3519af116 100644 --- a/InfoLogger/public/app.css +++ b/InfoLogger/public/app.css @@ -264,3 +264,10 @@ a.disabled { pointer-events: none; cursor: default; } flex-direction: column; gap: 0.25rem; } + +#status-bar-auto-scroll { + display: inline-block; + padding: .25em .7em; + line-height: 1; + margin: 0; +} From 0a0e6228b386534cc1cb9b101152277e0f1121ab Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Tue, 13 Oct 2026 10:59:29 +0200 Subject: [PATCH 6/7] Make auto-scroll status a toggle button of sorts Adds new setter for autoScrollLive. Shows a success/warning button in each state. Drops unneeded CSS. Rewrites test names and add one for disabling via the button. --- InfoLogger/public/app.css | 7 ---- InfoLogger/public/log/Log.js | 7 ++-- InfoLogger/public/log/statusBar.js | 41 ++++++++++++---------- InfoLogger/test/public/status-bar-mocha.js | 20 ++++++++--- 4 files changed, 43 insertions(+), 32 deletions(-) diff --git a/InfoLogger/public/app.css b/InfoLogger/public/app.css index 3519af116..b3decfe96 100644 --- a/InfoLogger/public/app.css +++ b/InfoLogger/public/app.css @@ -264,10 +264,3 @@ a.disabled { pointer-events: none; cursor: default; } flex-direction: column; gap: 0.25rem; } - -#status-bar-auto-scroll { - display: inline-block; - padding: .25em .7em; - line-height: 1; - margin: 0; -} diff --git a/InfoLogger/public/log/Log.js b/InfoLogger/public/log/Log.js index db9af7368..98f334a32 100644 --- a/InfoLogger/public/log/Log.js +++ b/InfoLogger/public/log/Log.js @@ -575,10 +575,11 @@ export default class Log extends Observable { } /** - * Enable auto-scroll for live mode and scroll to the bottom of the table + * Set auto-scroll for live mode and notify the UI + * @param {boolean} enabled - whether auto-scroll should be enabled */ - enableAutoScrollLiveAndNotify() { - this.autoScrollLive = true; + setAutoScrollLive(enabled) { + this.autoScrollLive = enabled; this.notify(); } diff --git a/InfoLogger/public/log/statusBar.js b/InfoLogger/public/log/statusBar.js index bbcfa3d26..d72286c29 100644 --- a/InfoLogger/public/log/statusBar.js +++ b/InfoLogger/public/log/statusBar.js @@ -98,30 +98,35 @@ const applicationMessage = (model) => model.log.list.length > model.log.applicat * @param {Model} model - root model of the application * @returns {vnode} - the view of the application options */ -const applicationOptions = (model) => [ - model.log.isLiveModeRunning() && [ - model.log.autoScrollLive - ? h('span.success', { id: 'status-bar-auto-scroll', title: 'Autoscroll is enabled' }, 'Autoscroll Enabled') - : h( - 'button.btn.btn-sm.btn-warning', +const applicationOptions = (model) => { + const { log } = model; + const autoScrollEnabled = log.autoScrollLive; + return [ + log.isLiveModeRunning() && [ + h( + `button.btn.btn-sm${autoScrollEnabled ? '.btn-success' : '.btn-warning'}`, { id: 'status-bar-auto-scroll', - title: 'Click here to re-enable autoscroll', - onclick: () => model.log.enableAutoScrollLiveAndNotify(), + title: autoScrollEnabled ? 'Click here to disable autoscroll' : 'Click here to re-enable autoscroll', + onclick: (e) => { + e.currentTarget.blur(); + log.setAutoScrollLive(!autoScrollEnabled); + }, }, [ - iconWarning(), - h('span.d-inline', 'Autoscroll Disabled'), + !autoScrollEnabled && iconWarning(), + h('span.d-inline', autoScrollEnabled ? 'Autoscroll Enabled' : 'Autoscroll Disabled'), ], ), - h('span.mh1'), - ], - h('label.d-inline', { title: 'Show details of selected log' }, h('input', { - type: 'checkbox', - checked: model.inspectorEnabled, - onchange: () => model.toggleInspector(), - }), ' Inspector'), -]; + h('span.mh1'), + ], + h('label.d-inline', { title: 'Show details of selected log' }, h('input', { + type: 'checkbox', + checked: model.inspectorEnabled, + onchange: () => model.toggleInspector(), + }), ' Inspector'), + ]; +}; /** * Status for query mode, showing how many logs are really in DB and how much time it took to load it diff --git a/InfoLogger/test/public/status-bar-mocha.js b/InfoLogger/test/public/status-bar-mocha.js index 5335c6638..bc11f6756 100644 --- a/InfoLogger/test/public/status-bar-mocha.js +++ b/InfoLogger/test/public/status-bar-mocha.js @@ -361,16 +361,28 @@ describe('Status Bar test-suite', async () => { await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Enabled'); }); - it('should show correct button when autoscroll is disabled', async () => { + it('should show correct label when autoscroll is disabled', async () => { await page.evaluate(() => { - model.log.autoScrollLive = false; + model.log.setAutoScrollLive(false); }); await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); }); - it('should re-enable autoscroll when disabled autoscroll button is clicked', async () => { + it('should disable auto-scroll when clicked whilst auto-scroll is enabled', async () => { await page.evaluate(() => { - model.log.autoScrollLive = false; + model.log.setAutoScrollLive(true); + }); + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Enabled'); + + await page.click(AUTOSCROLL_SELECTOR); + + await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); + assert.strictEqual(await page.evaluate(() => model.log.autoScrollLive), false); + }); + + it('should enable auto-scroll when clicked whilst auto-scroll is disabled', async () => { + await page.evaluate(() => { + model.log.setAutoScrollLive(false); }); await waitForTextInElement(page, AUTOSCROLL_SELECTOR, 'Autoscroll Disabled'); From 1106d9c63260741f09876fcaf44625c1f7b72348 Mon Sep 17 00:00:00 2001 From: Isaac Hill <71404865+isaachilly@users.noreply.github.com> Date: Tue, 13 Oct 2026 11:03:34 +0200 Subject: [PATCH 7/7] Use new setAutoScrollLive method --- InfoLogger/public/log/tableLogsContent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InfoLogger/public/log/tableLogsContent.js b/InfoLogger/public/log/tableLogsContent.js index b72db7a20..13c822940 100644 --- a/InfoLogger/public/log/tableLogsContent.js +++ b/InfoLogger/public/log/tableLogsContent.js @@ -211,7 +211,7 @@ const tableContainerHooks = (model) => ({ const { height } = container.getBoundingClientRect(); const scrollTop = Math.max(container.scrollTop, 0); // cancel negative position due to Safari bounce scrolling if (container.scrollTop < model.log.scrollTop) { - model.log.autoScrollLive = false; // stop auto-scrolling if user scrolls up + model.log.setAutoScrollLive(false); // stop auto-scrolling if user scrolls up } model.log.setScrollTop(scrollTop, height); };