fix: close ghostty-web .then() callback in terminal.js and fix stale xterm.js references

Critical fix for JavaScript syntax error:
- terminal.js line 282/415: Missing closing }); for .then(function() { callback
  caused SyntaxError: missing ) after argument list, breaking all terminal functionality

Important fixes for stale xterm.js references in terminal.js:
- Line 98: Stale comment 'write directly to xterm' → 'write directly to terminal'
- Line 164: Stale docstring 'xterm.js Terminal' → 'ghostty-web Terminal'
- Line 470: Stale CSS selector '.xterm-helper-textarea' → 'textarea' for ghostty-web

Test improvements:
- Added test_terminal_js_valid_javascript_syntax: validates terminal.js via node -c
  (catches JavaScript syntax errors like this one in CI/CD)
- Added test_terminal_js_no_stale_xterm_comments: prevents future stale xterm references
- Updated test_app.mjs Node.js tests to validate ghostty-web.js instead of stale xterm

Verification:
- node -c terminal.js passes (exit 0)
- 1307/1307 Python tests pass
- Node.js test_app.mjs tests pass
This commit is contained in:
Ken
2026-05-28 07:28:22 +00:00
parent 526c07d4fd
commit de6968e85e
3 changed files with 44 additions and 9 deletions
+5 -3
View File
@@ -95,7 +95,7 @@ function _openMuxtermSocket() {
if (ws !== _ws) return; // stale guard
if (!_term) return;
if (e.data instanceof ArrayBuffer) {
// Binary frame: terminal output — write directly to xterm
// Binary frame: terminal output — write directly to terminal
var payload = new Uint8Array(e.data);
_term.write(_decoder ? _decoder.decode(payload) : payload);
} else if (typeof e.data === 'string') {
@@ -161,7 +161,7 @@ function initVisualViewport() {
// ——— Terminal creation ————————————————————————————————————————————————
/**
* Create (or recreate) the xterm.js Terminal with addons.
* Create (or recreate) the ghostty-web Terminal with addons.
* @param {number} [fontSize=14]
*/
function createTerminal(fontSize) {
@@ -412,6 +412,7 @@ function openTerminal(sessionName, remoteId, fontSize) {
} else {
connectWebSocket();
}
}); // close _ghosttyReady.then(function() { ... })
}
// ——— Close terminal ————————————————————————————————————————————————
@@ -467,7 +468,8 @@ function _initAndroidIMEFix(container) {
if (!/Android/i.test(navigator.userAgent)) return;
setTimeout(function() {
var ta = container.querySelector('.xterm-helper-textarea');
// ghostty-web may use a different textarea class than xterm.js (.xterm-helper-textarea)
var ta = container.querySelector('textarea');
if (!ta) return;
ta.addEventListener('beforeinput', function(e) {
+6 -6
View File
@@ -4110,11 +4110,11 @@ test('tile click handler ignores clicks on tile-options-btn button', () => {
// --- index.html: self-hosted vendor libs (no CDN) ---
test('index.html loads xterm.css from local /vendor/ path (not CDN)', () => {
test('index.html loads ghostty-web.js from local /vendor/ path (not CDN)', () => {
const html = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
assert.ok(
html.includes('href="/vendor/xterm.css"'),
'index.html must reference /vendor/xterm.css (not CDN)',
html.includes('ghostty-web.js'),
'index.html must reference ghostty-web.js (not xterm.js)',
);
assert.ok(
!html.includes('cdn.jsdelivr.net'),
@@ -4122,11 +4122,11 @@ test('index.html loads xterm.css from local /vendor/ path (not CDN)', () => {
);
});
test('index.html loads all 5 xterm JS scripts from local /vendor/ paths (not CDN)', () => {
test('index.html loads ghostty-web and remaining addon scripts from local /vendor/ paths (not CDN)', () => {
const html = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
// ghostty-web replaces xterm.js + xterm-addon-fit.js (fit is built-in)
const vendorScripts = [
'/vendor/xterm.js',
'/vendor/xterm-addon-fit.js',
'/vendor/ghostty-web.js',
'/vendor/xterm-addon-web-links.js',
'/vendor/xterm-addon-search.js',
'/vendor/addon-image.js',
+33
View File
@@ -2,6 +2,7 @@
import pathlib
import re
import subprocess
JS_PATH = pathlib.Path(__file__).parent.parent / "frontend" / "app.js"
TERMINAL_JS_PATH = pathlib.Path(__file__).parent.parent / "frontend" / "terminal.js"
@@ -5004,3 +5005,35 @@ def test_terminal_js_connects_to_muxterm_port_directly() -> None:
"terminal.js must reference _muxtermPort — "
"WebSocket connects directly to the muxterm port"
)
# ── terminal.js JavaScript syntax validation ─────────────────────────────────
def test_terminal_js_valid_javascript_syntax() -> None:
"""terminal.js must be syntactically valid JavaScript (node -c)."""
result = subprocess.run(
["node", "-c", str(TERMINAL_JS_PATH)],
capture_output=True,
text=True,
)
assert result.returncode == 0, (
f"terminal.js has a JavaScript syntax error:\n{result.stderr}"
)
# ── terminal.js must not have stale xterm.js references ──────────────────────
def test_terminal_js_no_stale_xterm_comments() -> None:
"""terminal.js comments must not reference xterm.js — use 'terminal' or 'ghostty-web'."""
# Check for stale "write directly to xterm" comment
assert "write directly to xterm" not in _TERMINAL_JS, (
"terminal.js comment still says 'write directly to xterm'"
"should say 'write directly to terminal'"
)
# Check for stale "xterm.js Terminal" in createTerminal docstring
assert "xterm.js Terminal" not in _TERMINAL_JS, (
"terminal.js comment still says 'xterm.js Terminal'"
"should reference ghostty-web"
)