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:
@@ -95,7 +95,7 @@ function _openMuxtermSocket() {
|
|||||||
if (ws !== _ws) return; // stale guard
|
if (ws !== _ws) return; // stale guard
|
||||||
if (!_term) return;
|
if (!_term) return;
|
||||||
if (e.data instanceof ArrayBuffer) {
|
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);
|
var payload = new Uint8Array(e.data);
|
||||||
_term.write(_decoder ? _decoder.decode(payload) : payload);
|
_term.write(_decoder ? _decoder.decode(payload) : payload);
|
||||||
} else if (typeof e.data === 'string') {
|
} else if (typeof e.data === 'string') {
|
||||||
@@ -161,7 +161,7 @@ function initVisualViewport() {
|
|||||||
|
|
||||||
// ——— Terminal creation ————————————————————————————————————————————————
|
// ——— Terminal creation ————————————————————————————————————————————————
|
||||||
/**
|
/**
|
||||||
* Create (or recreate) the xterm.js Terminal with addons.
|
* Create (or recreate) the ghostty-web Terminal with addons.
|
||||||
* @param {number} [fontSize=14]
|
* @param {number} [fontSize=14]
|
||||||
*/
|
*/
|
||||||
function createTerminal(fontSize) {
|
function createTerminal(fontSize) {
|
||||||
@@ -412,6 +412,7 @@ function openTerminal(sessionName, remoteId, fontSize) {
|
|||||||
} else {
|
} else {
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
}
|
}
|
||||||
|
}); // close _ghosttyReady.then(function() { ... })
|
||||||
}
|
}
|
||||||
|
|
||||||
// ——— Close terminal ————————————————————————————————————————————————
|
// ——— Close terminal ————————————————————————————————————————————————
|
||||||
@@ -467,7 +468,8 @@ function _initAndroidIMEFix(container) {
|
|||||||
if (!/Android/i.test(navigator.userAgent)) return;
|
if (!/Android/i.test(navigator.userAgent)) return;
|
||||||
|
|
||||||
setTimeout(function() {
|
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;
|
if (!ta) return;
|
||||||
|
|
||||||
ta.addEventListener('beforeinput', function(e) {
|
ta.addEventListener('beforeinput', function(e) {
|
||||||
|
|||||||
@@ -4110,11 +4110,11 @@ test('tile click handler ignores clicks on tile-options-btn button', () => {
|
|||||||
|
|
||||||
// --- index.html: self-hosted vendor libs (no CDN) ---
|
// --- 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');
|
const html = fs.readFileSync(new URL('../index.html', import.meta.url), 'utf8');
|
||||||
assert.ok(
|
assert.ok(
|
||||||
html.includes('href="/vendor/xterm.css"'),
|
html.includes('ghostty-web.js'),
|
||||||
'index.html must reference /vendor/xterm.css (not CDN)',
|
'index.html must reference ghostty-web.js (not xterm.js)',
|
||||||
);
|
);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
!html.includes('cdn.jsdelivr.net'),
|
!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');
|
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 = [
|
const vendorScripts = [
|
||||||
'/vendor/xterm.js',
|
'/vendor/ghostty-web.js',
|
||||||
'/vendor/xterm-addon-fit.js',
|
|
||||||
'/vendor/xterm-addon-web-links.js',
|
'/vendor/xterm-addon-web-links.js',
|
||||||
'/vendor/xterm-addon-search.js',
|
'/vendor/xterm-addon-search.js',
|
||||||
'/vendor/addon-image.js',
|
'/vendor/addon-image.js',
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
|
|
||||||
JS_PATH = pathlib.Path(__file__).parent.parent / "frontend" / "app.js"
|
JS_PATH = pathlib.Path(__file__).parent.parent / "frontend" / "app.js"
|
||||||
TERMINAL_JS_PATH = pathlib.Path(__file__).parent.parent / "frontend" / "terminal.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 — "
|
"terminal.js must reference _muxtermPort — "
|
||||||
"WebSocket connects directly to the muxterm port"
|
"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"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user