# Tabby Terminal Hyperlink Detection Architecture: A Deep Dive into the Linkifier Module

> Explore Tabby's three-layer hyperlink detection architecture combining XTerm addon and Angular dependency injection for real-time link matching in terminal output.

- Repository: [Eugene/tabby](https://github.com/Eugeny/tabby)
- Tags: architecture
- Published: 2026-03-03

---

**Tabby detects clickable terminal links using a three-layer architecture that combines XTerm's web-links addon with Angular dependency injection, allowing handlers to register custom regex patterns that are merged into a single detection regex and matched against terminal output in real-time.**

Tabby's terminal hyperlink detection architecture provides a modular, extensible system for identifying and opening URLs, file paths, and IP addresses directly from terminal output. Built on top of the XTerm.js frontend and the `@xterm/addon-web-links` addon, this implementation in the `Eugeny/tabby` repository uses Angular's dependency injection to register link handlers dynamically. The architecture separates detection logic from handling logic, making it trivial to add support for new link types without modifying core terminal code.

## The Three-Layer Detection Architecture

The hyperlink detection system in `tabby-linkifier` operates through three coordinated layers that handle pattern matching, validation, and execution.

### Layer 1: Link Decorators

The `LinkHighlighterDecorator` class in [`tabby-linkifier/src/decorator.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-linkifier/src/decorator.ts) serves as the bridge between XTerm and Tabby's handler system. This decorator creates a `WebLinksAddon` instance and attaches it to the terminal's XTerm frontend. It constructs a combined regular expression by merging patterns from all registered link handlers, then passes this as `urlRegex` to the addon. The decorator also implements `willHandleEvent` to respect user-configured modifier keys before triggering link opens.

### Layer 2: Link Handlers

Each link type is processed by a dedicated handler extending the abstract `LinkHandler` class defined in [`tabby-linkifier/src/api.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-linkifier/src/api.ts). Built-in implementations in [`tabby-linkifier/src/handlers.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-linkifier/src/handlers.ts) include `URLHandler`, `IPHandler`, `UnixFileHandler`, and `WindowsFileHandler`. Every handler declares a `regex` property for pattern matching, a `priority` value for execution order, and implements three key methods: `handle` to execute the action, `verify` to validate matches, and `convert` to transform raw text into valid URIs.

### Layer 3: Configuration and Dependency Injection

The `LinkifierModule` in [`tabby-linkifier/src/index.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-linkifier/src/index.ts) uses Angular's provider system to register all handlers and the decorator. The `ConfigService` reads the `clickableLinks.modifier` setting (e.g., `ctrlKey`) from user preferences, which `LinkHighlighterDecorator.willHandleEvent` checks before processing mouse events. This DI-based approach ensures handlers are automatically discovered and incorporated into the detection regex without manual registration.

## Key Source Files

| Component | File Path | Purpose |
|-----------|-----------|---------|
| Terminal decorator | [`tabby-linkifier/src/decorator.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-linkifier/src/decorator.ts) | Wires XTerm with `WebLinksAddon` and manages event handling |
| Handler API | [`tabby-linkifier/src/api.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-linkifier/src/api.ts) | Defines the abstract `LinkHandler` base class |
| Built-in handlers | [`tabby-linkifier/src/handlers.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-linkifier/src/handlers.ts) | Implements `URLHandler`, `IPHandler`, and file path handlers |
| Module registration | [`tabby-linkifier/src/index.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-linkifier/src/index.ts) | Angular module that registers all providers |

## Implementation Examples

### Wiring the Detection Layer

When the decorator attaches to a terminal tab, it instantiates `WebLinksAddon` with a combined regex and a callback that iterates through registered handlers:

```typescript
// Inside LinkHighlighterDecorator.attach()
const addon = new WebLinksAddon(
    async (event, uri) => {
        if (!this.willHandleEvent(event)) return
        openLink(uri)               // → iterates over all registered handlers
    },
    { urlRegex: regex }            // regex is the union of each handler's pattern
)
tab.frontend.xterm.loadAddon(addon)

```

The `openLink` function tests each handler's `fullMatchRegex` against the URI, optionally calls `handler.verify`, and finally invokes `handler.handle` to open the link.

### Creating a Custom Link Handler

New link types require only a class extending `LinkHandler` and registration in the module:

```typescript
import { Injectable } from '@angular/core'
import { LinkHandler } from 'tabby-linkifier'
import { BaseTerminalTabComponent } from 'tabby-terminal'

@Injectable()
export class TicketHandler extends LinkHandler {
    // Matches strings like "TICKET-1234"
    regex = /TICKET-\d+/
    priority = 6                     // higher than built-ins if desired

    handle (uri: string, tab?: BaseTerminalTabComponent<any>) {
        // Open a ticket-viewing web page
        this.platform.openExternal(`https://mytracker.example.com/${uri}`)
    }
}

// Register it in LinkifierModule
providers: [
    { provide: LinkHandler, useClass: TicketHandler, multi: true },
    // …other providers
]

```

### Respecting the Modifier Key

The decorator checks the configured modifier before handling click events:

```typescript
private willHandleEvent(event: MouseEvent) {
    const modifier = this.config.store.clickableLinks.modifier   // e.g. "ctrlKey"
    return !modifier || event[modifier]                         // only react if modifier is pressed
}

```

## Summary

- **Tabby's hyperlink detection** relies on the `WebLinksAddon` from XTerm.js, configured with a combined regex built from all registered handlers.
- **Link handlers** extend an abstract base class and implement `regex`, `priority`, `handle`, `verify`, and `convert` methods to process specific link types.
- **The decorator pattern** in `LinkHighlighterDecorator` connects the addon to Tabby's terminal tabs while respecting user-configured modifier keys via `ConfigService`.
- **Angular dependency injection** in `LinkifierModule` enables automatic discovery of handlers, making the system fully extensible without core code changes.

## Frequently Asked Questions

### How does Tabby combine multiple regex patterns for link detection?

The `LinkHighlighterDecorator` aggregates the `regex` property from every registered `LinkHandler` instance into a single combined pattern. This unified regex is passed to the `WebLinksAddon` as the `urlRegex` option, allowing the addon to match any supported link type in a single scan of the terminal output.

### What determines the order in which link handlers are executed?

Each handler declares a `priority` value, and handlers are sorted accordingly before execution. Higher priority handlers test their `fullMatchRegex` against the URI first. The first handler that matches and passes verification receives the `handle` call, preventing lower-priority handlers from processing the same text.

### How can I add support for a new link type in Tabby?

Create a class extending `LinkHandler` in [`tabby-linkifier/src/handlers.ts`](https://github.com/Eugeny/tabby/blob/main/tabby-linkifier/src/handlers.ts) or a custom plugin, define the `regex` pattern and `priority`, implement the `handle` method to open the link, and register the class as a provider in `LinkifierModule` with the token `LinkHandler` and `multi: true`. The decorator automatically includes your handler's regex in the detection pattern.

### Where does Tabby store the configurable modifier key for clickable links?

The modifier key configuration is stored in `ConfigService` under the key `clickableLinks.modifier`, which accepts values like `"ctrlKey"`, `"altKey"`, or `"metaKey"`. The `LinkHighlighterDecorator.willHandleEvent` method reads this value and checks the corresponding property on the `MouseEvent` before allowing link activation.