linkquisition

package module
v1.17.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 5, 2026 License: MIT Imports: 11 Imported by: 0

README

Linkquisition

coverage

Linkquisition is a fast, configurable browser-picker for Linux (and experimentally macOS) written in Go.

...as nobody expects the Linkquisition!

screenshot

What is it?

Motivation behind this project is:

  1. I needed a fast browser-picker for Linux desktop that is configurable to automatically choose a browser based on different rules
  2. I have written a lot of server-side code in Go and wanted to see how easy it is to write a desktop app in Go

Features

  • Fast
  • Configurable
    • Automatically chooses a browser based on different rules
      • domain (e.g. example.com)
      • site (e.g. www.example.com)
      • regular expression (e.g. .*\.example\.com)
    • Hide a browser from the list
    • Manually add a browser to the list (for example, to open a URL in a different profile)
    • Remember the choice for given site
  • keyboard-shortcuts
    • Enter to open the URL in the default browser
    • Ctrl+C to just copy the URL to clipboard and close the window
    • Number keys (1-9) to select a browser

Installation

Linux

You can download the latest .deb package from the releases page.

The installation contains everything needed to launch the application using the desktop-environment, e.g. you should be able to press Super-key in Ubuntu and type "Linkquisition" to see the launcher. To do the same in terminal just run linkquisition command. Launching the application without any arguments will show the configuration screen which allows you to set it as the default browser and scan for installed browsers for faster startup and easier configuration.

macOS (experimental)

Download the latest Linkquisition_macOS_arm64.zip from the releases page, unzip it, and move Linkquisition.app to /Applications.

Since the app is not notarized with Apple, macOS will block it on first launch. To fix this, run:

xattr -cr /Applications/Linkquisition.app

Configuration

Linkquisition configuration screen

As mentioned in the installation section, you can launch the application without any arguments to show the configuration screen.

To set Linkquisition as the default browser, you can click the "Set as default" button and after this any links opened (outside browsers) will either show you the screen to choose a browser, or open one automatically if configured so.

The configuration file is located at ~/.config/linkquisition/config.json and clicking the "Scan browsers" button will create one if it does not exist, or update it with the currently installed browsers. Re-scanning later will not remove any manually added browsers or rules to existing browsers.

If adding a browser-entry manually to the config.json be sure to mark it as "manual" to prevent it from being removed on next scan. Also, if you want to hide a browser from the list, you can have it's "hidden" -attribute with value true.

Please note that the scan will use the "command" -attribute as the identifier for the browser, so if change the command it will be treated as a different browser and might be removed if not safe-guarded with "source": "manual" -setting.

An example config.json -file
{
  "locale": "",
  "browsers": [
    {
      "name": "Microsoft Edge",
      "command": "/usr/bin/microsoft-edge-stable %U",
      "hidden": false,
      "source": "auto",
      "matches": [
        {
          "type": "site",
          "value": "www.office.com"
        }
      ]
    },
    {
      "name": "Firefox",
      "command": "firefox %u",
      "hidden": false,
      "source": "auto",
      "matches": [
        {
          "type": "site",
          "value": "www.facebook.com"
        }
      ]
    }
  ]
}
Localization

Linkquisition supports localization. The UI language is determined in the following order:

  1. If locale is set in config.json (e.g. "locale": "fi"), that locale is used
  2. Otherwise, the system locale is auto-detected
  3. If no matching translation is available, English is used as fallback

Currently supported languages:

  • English (en) — default
  • Finnish (fi)
  • Spanish (es)
  • Swedish (sv)

To contribute a new translation, add a JSON file to internal/i18n/translations/ following the format of the existing files (e.g. en.json). The filename should be the locale code (e.g. de.json for German).

Command-line interface

In addition to the GUI, Linkquisition provides a full CLI for scriptable configuration:

# Show help
linkquisition --help

# Version
linkquisition --version

# Configuration
linkquisition config                        # show full config as JSON
linkquisition config get logLevel           # get a single value
linkquisition config set logLevel debug     # set a value
linkquisition config path                   # print config file path

# Plugin management
linkquisition plugin list                   # list configured + available plugins
linkquisition plugin enable <name>          # enable a plugin
linkquisition plugin disable <name>         # disable a plugin
linkquisition plugin add <name>             # add an available plugin with defaults

# Browser management
linkquisition browsers list                 # list configured browsers
linkquisition browsers scan                 # scan system for installed browsers

# Match rules
linkquisition rule list                     # list all URL match rules
linkquisition rule list firefox             # list rules for a specific browser
linkquisition rule add firefox site github.com        # add a rule
linkquisition rule add chrome regex ".*\.example\.com"  # add a regex rule
linkquisition rule remove firefox 1         # remove rule by index

# Set as default browser
linkquisition set-default

On macOS, the binary is located at /Applications/Linkquisition.app/Contents/MacOS/linkquisition. You can create a symlink for convenience:

ln -s /Applications/Linkquisition.app/Contents/MacOS/linkquisition /usr/local/bin/linkquisition

Development

I am using Ubuntu Linux for development, so the instructions are tailored for that. However, the code should work on any Freedesktop.org-compliant Linux distribution, although I have not tested it. Also, I have limited the architecture to amd64, as I do not have time/access to other architectures for testing easily.

Requirements
Building locally

The following command will build a binary in the bin directory:

task build # results in bin/linkquisition-linux-amd64

To run in watch mode:

task build --watch # results in bin/linkquisition-linux-amd64 (rebuilds on any relevant file change)
Packaging locally

Packaging locally is for testing purposes only, actual packaging should be done in a CI/CD pipeline, which currently is Github.com Actions.

The following command will build a .deb package in the dist directory:

# export VERSION=0.1.0-dev # optional, if not set, defaults to 0.0.0
task package:deb # results in dist/linkquisition_0.0.0_amd64.deb

Experimental plugin-system

See plugins for more information.

TODO

  • Add support for plugins
  • Add support for translations
  • Add support for browser icons
  • Add support for more platforms
  • Add support for more architectures
  • Add support for more package-formats
Linkquisition

With the above list the most interesting feature for me personally is the plugins -feature, as it would allow for doing some more complex processing of the URL before opening it in a browser. For example, I could write a plugin that strips any tracking parameters from the URL before opening it in the browser. See Sanitize -plugin for more information.

I also would like to have a plugin that checks if the opened url is a Microsoft Defender (Evergreen) URL and then, with matching rules, opens the actual url (baked in the "evergreen-assets URL") in a browser. This way all the internal links in my company could be opened directly in the browser, but the external links would still go through the Defender URL. See Unwrap -plugin for more information.

Documentation

Index

Constants

View Source
const (
	BrowserMatchTypeRegex  = "regex"
	BrowserMatchTypeDomain = "domain"
	BrowserMatchTypeSite   = "site"

	SourceAuto   = "auto"
	SourceManual = "manual"

	LogLevelDebug = "debug"
	LogLevelInfo  = "info"
	LogLevelWarn  = "warn"
	LogLevelError = "error"
)

Variables

View Source
var ErrNoMatchFound = errors.New("no match found")

Functions

func MapSettingsLogLevelToSlog

func MapSettingsLogLevelToSlog(logLevel string) slog.Level

Types

type Browser

type Browser struct {
	Name    string
	Command string
}

type BrowserMatch

type BrowserMatch struct {
	Type  string `json:"type"`
	Value string `json:"value"`
	// contains filtered or unexported fields
}

type BrowserService

type BrowserService interface {
	// GetAvailableBrowsers returns a list of available browsers in the system
	GetAvailableBrowsers() ([]Browser, error)

	// GetDefaultBrowser returns the default browser in the system
	GetDefaultBrowser() (Browser, error)

	// OpenUrlWithDefaultBrowser launches the given url with the default system browser
	OpenUrlWithDefaultBrowser(url string) error

	// OpenUrlWithBrowser launches the given url with the given browser
	OpenUrlWithBrowser(url string, browser *Browser) error

	// AreWeTheDefaultBrowser returns true if Linkquisition is the default browser
	AreWeTheDefaultBrowser() bool

	// MakeUsTheDefaultBrowser sets Linkquisition as the default browser
	MakeUsTheDefaultBrowser() error

	// GetIconForBrowser returns the icon for the given browser
	GetIconForBrowser(browser Browser) ([]byte, error)
}

type BrowserSettings

type BrowserSettings struct {
	Name    string `json:"name"`
	Command string `json:"command"`
	Hidden  bool   `json:"hidden"`
	Source  string `json:"source"`

	Matches []BrowserMatch `json:"matches"`
}

func (*BrowserSettings) CompileRegexMatches added in v1.13.0

func (s *BrowserSettings) CompileRegexMatches()

CompileRegexMatches pre-compiles all regex match patterns for this browser. Invalid patterns are logged and will be skipped during matching.

func (*BrowserSettings) MatchesUrl

func (s *BrowserSettings) MatchesUrl(u string) bool

MatchesUrl returns true if the given url matches any of the browser's rules

type FileSettingsService added in v1.11.1

type FileSettingsService struct {
	BrowserService BrowserService
	PathProvider   PathProvider
}

FileSettingsService is the shared, platform-independent implementation of SettingsService.

func (*FileSettingsService) GetConfigFilePath added in v1.11.1

func (s *FileSettingsService) GetConfigFilePath() string

func (*FileSettingsService) GetConfigFolderPath added in v1.15.0

func (s *FileSettingsService) GetConfigFolderPath() string

func (*FileSettingsService) GetLogFilePath added in v1.11.1

func (s *FileSettingsService) GetLogFilePath() string

func (*FileSettingsService) GetLogFolderPath added in v1.11.1

func (s *FileSettingsService) GetLogFolderPath() string

func (*FileSettingsService) GetPluginFolderPath added in v1.11.1

func (s *FileSettingsService) GetPluginFolderPath() string

func (*FileSettingsService) GetSettings added in v1.11.1

func (s *FileSettingsService) GetSettings() *Settings

func (*FileSettingsService) IsConfigured added in v1.11.1

func (s *FileSettingsService) IsConfigured() (bool, error)

func (*FileSettingsService) ReadSettings added in v1.11.1

func (s *FileSettingsService) ReadSettings() (*Settings, error)

func (*FileSettingsService) ScanBrowsers added in v1.11.1

func (s *FileSettingsService) ScanBrowsers() error

func (*FileSettingsService) WriteSettings added in v1.11.1

func (s *FileSettingsService) WriteSettings(settings *Settings) error

type PathProvider added in v1.11.1

type PathProvider interface {
	GetConfigFolderPath() string
	GetLogFolderPath() string
	GetPluginFolderPath() string
}

PathProvider supplies platform-specific paths used by the SettingsService.

type Plugin

type Plugin interface {
	// Setup is called when the plugin is being setup
	Setup(serviceProvider PluginServiceProvider, config map[string]interface{})

	// ModifyUrl is called just before the URL is being matched against the browser-rules
	// The plugin can modify the URL and return it (or otherwise just return the original URL)
	ModifyUrl(url string) string

	// Shutdown is called when the application is about to exit.
	// Plugins should use this to finish any background work (e.g. writing files).
	// The context carries a deadline — plugins must return before it expires.
	Shutdown(ctx context.Context)
}

Plugin is an interface that all plugins must implement

type PluginServiceProvider

type PluginServiceProvider interface {
	GetLogger() *slog.Logger
	GetSettings() *Settings
	GetConfigFolderPath() string
}

PluginServiceProvider is an interface that provides the logger and settings to the plugin This is passed to the plugin as a dependency when being setup.

func NewPluginServiceProvider

func NewPluginServiceProvider(logger *slog.Logger, settings *Settings, configFolderPath string) PluginServiceProvider

type PluginSettings

type PluginSettings struct {
	// Path is the path to the plugin binary
	Path string `json:"path"`

	// IsDisabled allows temporarily disabling individual plugins
	IsDisabled bool `json:"isDisabled"`

	Settings map[string]interface{} `json:"settings,omitempty"`
}

type Settings

type Settings struct {
	Locale   string            `json:"locale,omitempty"`
	LogLevel string            `json:"logLevel,omitempty"`
	Browsers []BrowserSettings `json:"browsers"`
	Plugins  []PluginSettings  `json:"plugins,omitempty"`
	Ui       UiSettings        `json:"ui,omitempty"`
}

func GetDefaultSettings

func GetDefaultSettings() *Settings

func (*Settings) AddRuleToBrowser added in v1.4.6

func (s *Settings) AddRuleToBrowser(b *Browser, matchType, matchValue string)

func (*Settings) CompileAllRegexMatches added in v1.13.0

func (s *Settings) CompileAllRegexMatches() *Settings

CompileAllRegexMatches pre-compiles regex patterns for all browsers. Call after loading settings to avoid repeated compilation during URL matching.

func (*Settings) GetMatchingBrowser

func (s *Settings) GetMatchingBrowser(u string) (*Browser, error)

func (*Settings) GetSelectableBrowsers

func (s *Settings) GetSelectableBrowsers() []Browser

func (*Settings) NormalizeBrowsers

func (s *Settings) NormalizeBrowsers() *Settings

NormalizeBrowsers moves hidden browsers to the end of the list

func (*Settings) UpdateWithBrowsers

func (s *Settings) UpdateWithBrowsers(browsers []Browser) *Settings

type SettingsService

type SettingsService interface {
	// IsConfigured returns true if the settings have been configured (i.e. the config-file exists)
	IsConfigured() (bool, error)

	// GetSettings returns the settings, either from the config-file or the default settings
	GetSettings() *Settings

	// ReadSettings reads the config-file and returns the settings
	ReadSettings() (*Settings, error)

	// WriteSettings writes the settings to the config-file
	WriteSettings(settings *Settings) error

	// ScanBrowsers scans (or re-scans) the system for available browsers and creates/updates the config-file
	ScanBrowsers() error

	// GetLogFilePath returns the absolute path to the log-file
	GetLogFilePath() string

	// GetLogFolderPath returns the absolute path to the log-folder
	GetLogFolderPath() string

	// GetPluginFolderPath returns the absolute path to the plugin-folder
	GetPluginFolderPath() string

	// GetConfigFilePath returns the absolute path to the config-file
	GetConfigFilePath() string

	// GetConfigFolderPath returns the absolute path to the config-folder
	GetConfigFolderPath() string
}

type URL

type URL struct {
	// contains filtered or unexported fields
}

func NewURL

func NewURL(u string) *URL

func (URL) GetDomain

func (u URL) GetDomain() (string, error)

func (URL) GetSite

func (u URL) GetSite() (string, error)

type UiSettings added in v1.4.6

type UiSettings struct {
	HideKeyboardGuideLabel bool `json:"hideKeyboardGuideLabel,omitempty"`
}

Directories

Path Synopsis
internal
i18n
Package i18n provides localization support for the application.
Package i18n provides localization support for the application.
plugins
defang command
sanitize command
terminus command
unwrap command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL