From 57b8c6290994a9e1bd4c035e5cd8b34db5e5347e Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 4 Feb 2026 10:54:48 -0600 Subject: [PATCH] fix(app): terminal hyperlink clicks --- packages/app/src/components/terminal.tsx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx index 11bcd4cc8..4ad79ee82 100644 --- a/packages/app/src/components/terminal.tsx +++ b/packages/app/src/components/terminal.tsx @@ -1,5 +1,6 @@ import type { Ghostty, Terminal as Term, FitAddon } from "ghostty-web" import { ComponentProps, createEffect, createSignal, onCleanup, onMount, splitProps } from "solid-js" +import { usePlatform } from "@/context/platform" import { useSDK } from "@/context/sdk" import { monoFontFamily, useSettings } from "@/context/settings" import { SerializeAddon } from "@/addons/serialize" @@ -52,6 +53,7 @@ const DEFAULT_TERMINAL_COLORS: Record<"light" | "dark", TerminalColors> = { } export const Terminal = (props: TerminalProps) => { + const platform = usePlatform() const sdk = useSDK() const settings = useSettings() const theme = useTheme() @@ -135,6 +137,22 @@ export const Terminal = (props: TerminalProps) => { focusTerminal() } + const handleLinkClick = (event: MouseEvent) => { + if (!event.shiftKey && !event.ctrlKey && !event.metaKey) return + if (event.altKey) return + if (event.button !== 0) return + + const t = term + if (!t) return + + const link = (t as unknown as { currentHoveredLink?: { text: string } }).currentHoveredLink + if (!link?.text) return + + event.preventDefault() + event.stopImmediatePropagation() + platform.openLink(link.text) + } + onMount(() => { const run = async () => { const loaded = await loadGhostty() @@ -240,6 +258,9 @@ export const Terminal = (props: TerminalProps) => { container.addEventListener("pointerdown", handlePointerDown) cleanups.push(() => container.removeEventListener("pointerdown", handlePointerDown)) + container.addEventListener("click", handleLinkClick, { capture: true }) + cleanups.push(() => container.removeEventListener("click", handleLinkClick, { capture: true })) + handleTextareaFocus = () => { t.options.cursorBlink = true }