From 20437b6e2901c7ee85ef1c5a290e5e4a832fc5a2 Mon Sep 17 00:00:00 2001
From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com>
Date: Tue, 27 Oct 2026 04:04:56 -0500
Subject: [PATCH] Keep the connect buttons disabled until the app has loaded
A click on Connect made before the app script had run hit an undefined
app and did nothing. The buttons now start disabled and are enabled once
the handlers are wired, so an early click waits instead of being lost.
---
src/ViperIDE.html | 8 +++---
src/app.js | 6 ++++
test/browser/early-connect.spec.js | 46 ++++++++++++++++++++++++++++++
3 files changed, 56 insertions(+), 4 deletions(-)
create mode 100644 test/browser/early-connect.spec.js
diff --git a/src/ViperIDE.html b/src/ViperIDE.html
index dc81094b..d7dbd762 100644
--- a/src/ViperIDE.html
+++ b/src/ViperIDE.html
@@ -32,10 +32,10 @@
-
-
-
-
+
+
+
+
diff --git a/src/app.js b/src/app.js
index 27bce287..244a368d 100644
--- a/src/app.js
+++ b/src/app.js
@@ -2606,6 +2606,12 @@ function showOfflineReadyToast(version) {
openWelcomeTab()
})
+ /* The connect buttons ship disabled: until this script has wired up the
+ terminal and the handlers, a click has nothing to act on. */
+ for (const t of ['sim', 'ws', 'ble', 'usb']) {
+ QID(`btn-conn-${t}`).disabled = false
+ }
+
setTimeout(() => {
document.body.classList.add('loaded')
}, 100)
diff --git a/test/browser/early-connect.spec.js b/test/browser/early-connect.spec.js
new file mode 100644
index 00000000..6eb9c980
--- /dev/null
+++ b/test/browser/early-connect.spec.js
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: MIT
+//
+// A click on Connect made the instant the page starts loading used to be
+// lost: the button was live before the script behind it had run, so the click
+// hit an undefined `app` and nothing happened. The buttons now stay disabled
+// until the app can honour the click, so an early click waits for that and
+// then connects.
+import { test, expect } from '@playwright/test'
+
+const DELAY_MS = 3000
+
+/* The app ships as one inline script at the end of index.html. To simulate a
+ slow load, serve the same page with that script parked in an inert block
+ and only started DELAY_MS later; the buttons above it are on screen the
+ whole time. */
+async function slowApp(page) {
+ await page.route('**/index.html', async (route) => {
+ const res = await route.fetch()
+ const html = await res.text()
+ const start = html.indexOf('')
+ const code = html.slice(start + '
+`
+ await route.fulfill({ response: res, body: html.slice(0, start) + late + html.slice(end + ''.length) })
+ })
+}
+
+test('connect clicked while the page is still loading connects once ready', async ({ page }) => {
+ await slowApp(page)
+ await page.goto('/index.html')
+ // Playwright waits for the button to be enabled before it clicks.
+ await page.locator('#btn-conn-sim').click()
+ await expect(page.locator('#xterm')).toContainText('>>>', { timeout: 45_000 })
+})
+
+test('connect buttons are disabled until the app is ready', async ({ page }) => {
+ await slowApp(page)
+ await page.goto('/index.html')
+ await expect(page.locator('#btn-conn-sim')).toBeDisabled()
+ await expect(page.locator('#btn-conn-sim')).toBeEnabled({ timeout: 15_000 })
+})