feat: clickable URLs in terminal — Ctrl/Cmd+Click opens in new tab

Loads xterm-addon-web-links (0.9.0) from CDN. URLs in terminal output
are auto-detected and underlined on hover. Ctrl+Click (Linux/Windows)
or Cmd+Click (macOS) opens the URL in a new browser tab. Plain click
preserved for normal terminal text selection.
This commit is contained in:
Brian Krabach
2026-04-01 14:34:18 -07:00
parent 2de19bd988
commit 2c30ad39a2
4 changed files with 43 additions and 0 deletions
+1
View File
@@ -246,6 +246,7 @@
<!-- ── Scripts ──────────────────────────────────────────────────────────── -->
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-web-links@0.9.0/lib/xterm-addon-web-links.js"></script>
<script src="/app.js" defer></script>
<script src="/terminal.js" defer></script>
</body>
+12
View File
@@ -243,6 +243,18 @@ function createTerminal() {
_fitAddon = new window.FitAddon.FitAddon();
_term.loadAddon(_fitAddon);
// Clickable URLs — Ctrl+Click (Windows/Linux) or Cmd+Click (macOS) opens in new tab.
// xterm-addon-web-links auto-detects URLs and adds hover underlines.
// Plain click is preserved for normal terminal text selection.
var WebLinksAddon = window.WebLinksAddon && window.WebLinksAddon.WebLinksAddon;
if (WebLinksAddon) {
_term.loadAddon(new WebLinksAddon(function(event, uri) {
if (event.ctrlKey || event.metaKey) {
window.open(uri, '_blank');
}
}));
}
}
// ─── Open / close ─────────────────────────────────────────────────────────────
+12
View File
@@ -949,4 +949,16 @@ test('terminal.js registers OSC 52 handler for tmux clipboard bridge', () => {
);
});
// --- Clickable URLs via xterm-addon-web-links ---
test('terminal.js loads xterm-addon-web-links for clickable URLs', () => {
const source = fs.readFileSync(new URL('../terminal.js', import.meta.url), 'utf8');
assert.ok(source.includes('WebLinksAddon'), 'must reference WebLinksAddon');
assert.ok(
source.includes('ctrlKey') || source.includes('metaKey'),
'must check modifier key for link clicks',
);
assert.ok(source.includes('window.open'), 'must open URLs in new tab');
});
+18
View File
@@ -1325,3 +1325,21 @@ def test_html_display_panel_no_view_scope() -> None:
assert el is None, (
"#setting-view-scope must NOT be in the display panel (moved to Multi-Device tab)"
)
# ============================================================
# Clickable URLs — xterm-addon-web-links (task: clickable URLs)
# ============================================================
def read_html() -> str:
"""Read raw HTML content of index.html."""
return HTML_PATH.read_text()
def test_html_loads_web_links_addon() -> None:
"""index.html must load the xterm-addon-web-links CDN script."""
html = read_html()
assert "web-links" in html.lower() or "weblinks" in html.lower(), (
"Must load xterm-addon-web-links from CDN"
)