summaryrefslogtreecommitdiff
path: root/modules/browser-config.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-10-23 13:10:12 -0500
committerCraig Jennings <c@cjennings.net>2025-10-23 13:10:12 -0500
commit98c4a10c9b2c0120fda0c541a89f67d3d953dd94 (patch)
tree3113c6d3dc2a46d88e5c7eef6b87cbaa32efd6e2 /modules/browser-config.el
parentb5c58c9e212d3e8174ec7f8808dbda1578ba22fd (diff)
feat:browser: Add browser configuration module
Introduce `browser-config.el` to handle browser selection and configuration within Emacs. This module discovers available browsers, allows user selection through `M-x cj/choose-browser`, and persists the choice while supporting all link types.
Diffstat (limited to 'modules/browser-config.el')
-rw-r--r--modules/browser-config.el132
1 files changed, 132 insertions, 0 deletions
diff --git a/modules/browser-config.el b/modules/browser-config.el
new file mode 100644
index 00000000..fddc02e6
--- /dev/null
+++ b/modules/browser-config.el
@@ -0,0 +1,132 @@
+;;; browser-config.el --- Browser Configuration -*- lexical-binding: t; coding: utf-8; -*-
+;; author: Craig Jennings <c@cjennings.net>
+
+;;; Commentary:
+;; This module provides browser selection and configuration for Emacs.
+;; It automatically discovers available browsers on the system, allows the user
+;; to choose their preferred browser via completing-read, and persists the choice
+;; to a file. Works with all link types including org-mode links.
+;;
+;; Interactive Commands:
+;; M-x cj/choose-browser - Select default browser from available options
+
+;;; Code:
+
+(require 'cl-lib)
+
+;; Persistence file for storing browser choice
+(defvar cj/browser-choice-file
+ (expand-file-name "browser-choice.el" user-emacs-directory)
+ "File to persist the user's browser choice.")
+
+;; Browser definitions: (executable-name browse-function display-name [program-var])
+;; Use nil for executable-name to indicate a built-in Emacs browser (always available)
+(defvar cj/browser-definitions
+ '((nil eww-browse-url "EWW (Emacs Browser)" nil)
+ ("google-chrome" browse-url-chrome "Google Chrome" browse-url-chrome-program)
+ ("google-chrome-stable" browse-url-chrome "Google Chrome" browse-url-chrome-program)
+ ("chrome" browse-url-chrome "Chrome" browse-url-chrome-program)
+ ("chromium" browse-url-chromium "Chromium" browse-url-chromium-program)
+ ("chromium-browser" browse-url-chromium "Chromium" browse-url-chromium-program)
+ ("firefox" browse-url-firefox "Firefox" browse-url-firefox-program)
+ ("brave" browse-url-chrome "Brave" browse-url-chrome-program)
+ ("brave-browser" browse-url-chrome "Brave Browser" browse-url-chrome-program)
+ ("microsoft-edge" browse-url-chrome "Microsoft Edge" browse-url-chrome-program)
+ ("vivaldi" browse-url-chrome "Vivaldi" browse-url-chrome-program)
+ ("opera" browse-url-chrome "Opera" browse-url-chrome-program)
+ ("qutebrowser" browse-url-generic "qutebrowser" browse-url-generic-program))
+ "List of browser definitions.
+Each entry is (EXECUTABLE BROWSE-FUNCTION DISPLAY-NAME [PROGRAM-VAR]).
+Use nil for EXECUTABLE to indicate a built-in Emacs browser (always available).")
+
+(defun cj/discover-browsers ()
+ "Discover available browsers on the system.
+Returns a list of plists with :executable, :function, :name, and :path.
+Includes built-in Emacs browsers (those with nil executable)."
+ (let ((found-browsers '())
+ (seen-names (make-hash-table :test 'equal)))
+ (dolist (def cj/browser-definitions)
+ (let* ((executable (nth 0 def))
+ (browse-fn (nth 1 def))
+ (display-name (nth 2 def))
+ (program-var (nth 3 def))
+ (path (when executable (executable-find executable)))
+ ;; Built-in browsers (nil executable) are always available
+ (available-p (or (null executable) path)))
+ (when (and available-p (not (gethash display-name seen-names)))
+ (puthash display-name t seen-names)
+ (push (list :executable executable
+ :function browse-fn
+ :name display-name
+ :path path
+ :program-var program-var)
+ found-browsers))))
+ (nreverse found-browsers)))
+
+(defun cj/save-browser-choice (browser-plist)
+ "Save BROWSER-PLIST to the persistence file."
+ (with-temp-file cj/browser-choice-file
+ (insert ";;; Browser choice - Auto-generated\n")
+ (insert (format "(setq cj/saved-browser-choice '%S)\n" browser-plist))))
+
+(defun cj/load-browser-choice ()
+ "Load browser choice from the persistence file.
+Returns the browser plist if found, nil otherwise."
+ (when (file-exists-p cj/browser-choice-file)
+ (condition-case nil
+ (progn
+ (load cj/browser-choice-file)
+ (when (boundp 'cj/saved-browser-choice)
+ cj/saved-browser-choice))
+ (error nil))))
+
+(defun cj/apply-browser-choice (browser-plist)
+ "Apply the browser settings from BROWSER-PLIST."
+ (when browser-plist
+ (let ((browse-fn (plist-get browser-plist :function))
+ (executable (plist-get browser-plist :executable))
+ (path (plist-get browser-plist :path))
+ (program-var (plist-get browser-plist :program-var)))
+ ;; Set the main browse-url function
+ (setq browse-url-browser-function browse-fn)
+ ;; Set the specific browser program variable if it exists
+ (when program-var
+ (set program-var (or path executable)))
+ (message "Default browser set to: %s" (plist-get browser-plist :name)))))
+
+(defun cj/choose-browser ()
+ "Interactively choose a browser from available options.
+Persists the choice for future sessions."
+ (interactive)
+ (let* ((browsers (cj/discover-browsers))
+ (choices (mapcar (lambda (b) (plist-get b :name)) browsers)))
+ (if (null browsers)
+ (message "No supported browsers found on system PATH")
+ (let* ((choice (completing-read "Choose default browser: " choices nil t))
+ (selected (cl-find-if (lambda (b)
+ (string= (plist-get b :name) choice))
+ browsers)))
+ (when selected
+ (cj/save-browser-choice selected)
+ (cj/apply-browser-choice selected))))))
+
+;; Initialize: Load saved choice or use first available browser
+(defun cj/initialize-browser ()
+ "Initialize browser configuration on startup."
+ (let ((saved-choice (cj/load-browser-choice)))
+ (if saved-choice
+ (cj/apply-browser-choice saved-choice)
+ ;; No saved choice - try to set first available browser silently
+ (let ((browsers (cj/discover-browsers)))
+ (when browsers
+ (cj/apply-browser-choice (car browsers))
+ (message "No browser configured. Using %s. Run M-x cj/choose-browser to change."
+ (plist-get (car browsers) :name)))))))
+
+;; Run initialization
+(cj/initialize-browser)
+
+(keymap-global-set "C-; B" #'cj/choose-browser)
+
+(provide 'browser-config)
+;;; browser-config.el ends here