From 966bebce562b6d81c44dfc912740f6291903a02c Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Fri, 9 Oct 2026 03:50:37 +0000 Subject: [PATCH] desktop smoke: poll for the selection test instead of fixed timing (0.3.2) The v0.3.1 release smoke (8 s, slower runner) checked before the split pane's shell had run printf: 'mouse mode none'. Inject DECSET 1003 straight into the focused pane's terminal, poll until it is on, and make the final check wait for the result. Co-Authored-By: Claude Opus 5.5 --- desktop/main.js | 34 ++++++++++++++++++++-------------- desktop/package-lock.json | 4 ++-- desktop/package.json | 2 +- desktop/src/app.js | 6 ++++-- package.json | 2 +- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/desktop/main.js b/desktop/main.js index debfe31..30c4fb6 100644 --- a/desktop/main.js +++ b/desktop/main.js @@ -334,32 +334,38 @@ if (!app.requestSingleInstanceLock()) { }, wait * 0.7); // Shift+drag must leave a selection that survives the mouse moving on, // even when the app tracks every motion (hqtui hover, DECSET 1003). + // The escape goes straight to the focused pane's terminal (no shell to + // wait for); poll, since on a slow runner the split pane comes up late. + const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); let selection = "not run"; - setTimeout(() => { - const p = [...ptys.values()].pop(); - if (p) p.write("clear; printf 'select me please\\n\\033[?1003h'\r"); - }, wait * 0.75); - setTimeout(async () => { - if (!win) return; + const selectionTest = new Promise((done) => setTimeout(done, wait * 0.75)).then(async () => { try { - const r = await win.webContents.executeJavaScript(`(() => { - const b = document.querySelector(".pane.focused .xterm-screen").getBoundingClientRect(); - return { x: Math.round(b.left + 4), y: Math.round(b.top + 6), mode: window.hqSmoke.mouseMode() }; - })()`); + let r; + for (let i = 0; i < 40 && win; i++) { + r = await win.webContents.executeJavaScript(`(() => { + const b = document.querySelector(".pane.focused .xterm-screen").getBoundingClientRect(); + return { x: Math.round(b.left + 4), y: Math.round(b.top + 6), ...window.hqSmoke.state() }; + })()`); + if (r.mode === "any") break; + if (r.ptyId !== undefined) win.webContents.send("pty:data", r.ptyId, "\x1b[H\x1b[2Kselect me please\x1b[?1003h"); + await sleep(150); + } + if (!win || !r) return; const send = (type, x, modifiers) => win.webContents.sendInputEvent({ type, x, y: r.y, button: "left", clickCount: 1, modifiers }); send("mouseDown", r.x, ["shift"]); for (let x = r.x; x <= r.x + 80; x += 10) send("mouseMove", x, ["shift", "leftButtonDown"]); send("mouseUp", r.x + 80, ["shift"]); - await new Promise((res) => setTimeout(res, 100)); + await sleep(100); for (let x = r.x + 80; x <= r.x + 140; x += 10) win.webContents.sendInputEvent({ type: "mouseMove", x, y: r.y + 20 }); - await new Promise((res) => setTimeout(res, 300)); - const sel = await win.webContents.executeJavaScript("window.hqSmoke.selection()"); + await sleep(300); + const sel = await win.webContents.executeJavaScript("window.hqSmoke.state().selection"); selection = r.mode !== "any" ? `mouse mode ${r.mode}, not any` : sel ? "" : "cleared after release"; } catch (err) { selection = `error: ${err && err.message}`; } - }, wait * 0.85); + }); setTimeout(async () => { + await selectionTest; const shot = process.env.HQTERM_DESKTOP_SMOKE_SHOT; if (shot && win) { try { diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 35ca7f9..8ee97d4 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "hqterm-desktop", - "version": "0.3.1", + "version": "0.3.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hqterm-desktop", - "version": "0.3.1", + "version": "0.3.2", "license": "MIT", "dependencies": { "@xterm/addon-fit": "^0.11.0", diff --git a/desktop/package.json b/desktop/package.json index a45ce45..8dd1b9a 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,7 +1,7 @@ { "name": "hqterm-desktop", "productName": "hqterm", - "version": "0.3.1", + "version": "0.3.2", "description": "hqterm desktop: a terminal window that shows hqtui/qc images and HD emoji (xterm.js + node-pty).", "license": "MIT", "author": "Profullstack, Inc. ", diff --git a/desktop/src/app.js b/desktop/src/app.js index 9c8b478..eeae7fe 100644 --- a/desktop/src/app.js +++ b/desktop/src/app.js @@ -37,8 +37,10 @@ // CI smoke test (main.js) checks the focused pane's selection through this. if (init.smoke) { window.hqSmoke = { - mouseMode: () => focusedRt()?.term.modes.mouseTrackingMode, - selection: () => focusedRt()?.term.getSelection() || "", + state: () => { + const rt = focusedRt(); + return { ptyId: rt?.ptyId, mode: rt?.term.modes.mouseTrackingMode, selection: rt?.term.getSelection() || "" }; + }, }; } diff --git a/package.json b/package.json index 70d24e5..6961ff2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hqterm", - "version": "0.3.1", + "version": "0.3.2", "description": "Terminal session manager: persistent hqsh sessions with images and HD emoji, built on hqtui.", "license": "MIT", "private": true,