diff --git a/package-lock.json b/package-lock.json index c8452a5..b571def 100644 --- a/package-lock.json +++ b/package-lock.json @@ -90,7 +90,8 @@ "uuid": "^9.0.0", "watcher": "^2.2.2", "webpack": "^5.75.0", - "xterm": "^4.19.0" + "xterm": "^4.19.0", + "xterm-addon-web-links": "^0.6.0" }, "devDependencies": { "@babel/core": "^7.20.5", @@ -23610,6 +23611,14 @@ "xterm": "^4.0.0" } }, + "node_modules/xterm-addon-web-links": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/xterm-addon-web-links/-/xterm-addon-web-links-0.6.0.tgz", + "integrity": "sha512-H6XzjWWZu8FBo+fnYpxdPk9w5M6drbsvwPEJZGRS38MihiQaVFpKlCMKdfRgDbKGE530tw1yH54rhpZfHgt2/A==", + "peerDependencies": { + "xterm": "^4.0.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -41238,6 +41247,12 @@ "dev": true, "requires": {} }, + "xterm-addon-web-links": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/xterm-addon-web-links/-/xterm-addon-web-links-0.6.0.tgz", + "integrity": "sha512-H6XzjWWZu8FBo+fnYpxdPk9w5M6drbsvwPEJZGRS38MihiQaVFpKlCMKdfRgDbKGE530tw1yH54rhpZfHgt2/A==", + "requires": {} + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 57c2ccf..d0dd7a1 100644 --- a/package.json +++ b/package.json @@ -170,6 +170,7 @@ "uuid": "^9.0.0", "watcher": "^2.2.2", "webpack": "^5.75.0", - "xterm": "^4.19.0" + "xterm": "^4.19.0", + "xterm-addon-web-links": "^0.6.0" } } diff --git a/src/components/terminal.tsx b/src/components/terminal.tsx index 6bb81b4..e19b51d 100644 --- a/src/components/terminal.tsx +++ b/src/components/terminal.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useRef } from 'react' import { Terminal } from 'xterm' import { FitAddon } from 'xterm-addon-fit' +import { WebLinksAddon } from 'xterm-addon-web-links'; import 'xterm/css/xterm.css' import { useAppSelector, useAppDispatch } from '../app/hooks' import { FullState } from '../features/window/state' @@ -13,6 +14,12 @@ export function XTermComponent({ height }: { height: number }) { const terminalRef = useRef(null) const terminal = useRef(null) const fitAddon = useRef(new FitAddon()) + const webLinksAddon = useRef(new WebLinksAddon( + (event: MouseEvent, url: string) => { + event.preventDefault() + connector.terminalClickLink(url) + }, + )) const handleIncomingData = (e: any, data: any) => { terminal.current!.write(data) @@ -30,6 +37,7 @@ export function XTermComponent({ height }: { height: number }) { }) terminal.current.loadAddon(fitAddon.current) + terminal.current.loadAddon(webLinksAddon.current) if (terminalRef.current) { terminal.current.open(terminalRef.current) @@ -41,6 +49,11 @@ export function XTermComponent({ height }: { height: number }) { connector.terminalInto(e) }) + terminal.current.onData((e) => { + connector.terminalInto(e) + }) + + connector.registerIncData(handleIncomingData) // Make the terminal's size and geometry fit the size of #terminal-container @@ -55,6 +68,7 @@ export function XTermComponent({ height }: { height: number }) { useEffect(() => { if (terminal.current != null) { terminal.current.loadAddon(fitAddon.current) + terminal.current.loadAddon(webLinksAddon.current) fitAddon.current.fit() } }, [height, terminal, fitAddon]) diff --git a/src/main/main.ts b/src/main/main.ts index a476fec..caed363 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -906,6 +906,12 @@ const createWindow = () => { } return null }) + + // click on the terminal link + ipcMain.handle('terminal-click-link', (event, data) => { + shell.openExternal(data); + }) + setupLSPs(store) const projectPathObj = store.get('projectPath') if ( diff --git a/src/preload.ts b/src/preload.ts index 1675211..b12ed42 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -189,6 +189,7 @@ const electronConnector = { ipcRenderer.removeListener('terminal-incData', callback) }, terminalInto: (data: any) => ipcRenderer.invoke('terminal-into', data), + terminalClickLink: (data: any) => ipcRenderer.invoke('terminal-click-link', data), terminalResize: (data: any) => ipcRenderer.invoke('terminal-resize', data), registerFileWasAdded: (callback: Callback) =>