Files
kandev/apps/web/hooks/use-terminal-link-handler.ts
T
José Almeida 06e5114174 feat: add clickable terminal links with configurable open behavior (#401)
* feat: add clickable terminal links with configurable open behavior

Add @xterm/addon-web-links to both passthrough and shell terminals,
enabling Cmd+Click on URLs. Users can configure whether links open
in a new browser tab (default) or the built-in browser panel via a
new setting in the Settings page.

* style: fix prettier formatting

* fix: address CodeRabbit review comments

- Extract applyTerminalLinkBehavior helper to reduce complexity
- Set default terminalLinkBehavior for first-time users in empty settings path
- Use storeApi.getState() for optimistic rollback to avoid stale closures
- Add beforeEach settings reset in E2E tests
- Use 4xx range assertion instead of hardcoded 500

* fix: return 400 for user settings validation errors

Add ErrValidation sentinel to user service and wrap validation errors
from apply* functions. Handler now returns 400 Bad Request instead of
500 Internal Server Error for invalid input. Update E2E test assertion
to enforce client error status (< 500).

* fix: remove cmd+click mention from terminal links description

* fix: mirror validation error handling in WS update path

* style: fix prettier formatting
2026-03-10 15:37:51 +00:00

35 lines
1.1 KiB
TypeScript

import { useCallback, useEffect, useRef } from "react";
import { useAppStore } from "@/components/state-provider";
import { useDockviewStore } from "@/lib/state/dockview-store";
/**
* Returns a stable callback for handling terminal link clicks.
* Reads the user's `terminalLinkBehavior` setting to decide whether
* to open URLs in a new browser tab or the built-in browser panel.
*/
export function useTerminalLinkHandler(): (event: MouseEvent, uri: string) => void {
const behaviorRef = useRef<"new_tab" | "browser_panel">("new_tab");
const behavior = useAppStore((s) => s.userSettings.terminalLinkBehavior);
useEffect(() => {
behaviorRef.current = behavior;
}, [behavior]);
return useCallback((_event: MouseEvent, uri: string) => {
if (behaviorRef.current === "browser_panel") {
const api = useDockviewStore.getState().api;
if (api) {
const browserId = `browser:${Date.now()}`;
api.addPanel({
id: browserId,
component: "browser",
title: "Browser",
params: { url: uri },
});
return;
}
}
window.open(uri, "_blank", "noopener,noreferrer");
}, []);
}