Skip to content

chiply/svg-line

Repository files navigation

svg-line

CI License: GPL-3.0

Render the tab-bar, tab-line, header-line and mode-line as SVG images instead of laid-out text. Supports Emacs 29.1+ (graphical only).

Overview

An SVG image can be any height and is positioned at exact pixel coordinates, so svg-line makes possible things the text engine cannot do uniformly:

  • Multi-line bars of arbitrary height (a 3-row tab-bar, a 2-row breadcrumb header-line, a multi-row mode-line).
  • Per-line left / centre / right alignment on every row — not just the last, and without the :align-to-on-a-non-final-line redisplay freeze.
  • Tab lines that wrap overflowing tabs onto new rows instead of truncating or horizontally scrolling, with optional single-row centring.
  • Interactive indicators: any element can carry a left-click action, a right-click menu, hover help and a hover highlight — across all four bars, including the otherwise–uncooperative tab-bar.

svg-line is the rendering engine only: it ships no content and no colours of its own. You supply a :content function and styling, then bind it to a target.

Installation

With elpaca (use-package)

(use-package svg-line
  :ensure (:host github :repo "chiply/svg-line"))

With straight.el (use-package)

(use-package svg-line
  :straight (:host github :repo "chiply/svg-line"))

Manual

(add-to-list 'load-path "/path/to/svg-line")
(require 'svg-line)

Quick start

Define a line and activate it:

(svg-line-define 'my-mode-line
  :target 'mode-line
  :layout 'lines
  :content (lambda ()
             ;; one or more rows; each is (LEFT . RIGHT) or a
             ;; (:left L :center C :right R) plist for a centred middle
             (list (cons (list (buffer-name))
                         (list (format-mode-line "%l:%c")))))
  :active  #'mode-line-window-selected-p
  :background (lambda () "#e7edf6")
  :foreground (lambda () "#2a4d77"))

(svg-line-activate 'my-mode-line)     ; M-x svg-line-deactivate / svg-line-toggle

Colour and font options accept a literal value or a zero-argument function evaluated on every render, so theme-dependent colours live in your config and the engine stays theme-agnostic.

Layouts

  • lines — rows of (LEFT . RIGHT) (or [LEFT CENTER RIGHT] / a :left/:center/:right plist). A segment is a string, a zero-argument function, a bound-variable symbol, or a token: (:svg-bar FRAC W FILL BG), (:svg-pie FRAC FILL BG), or an interactive (:svg-seg TEXT . PLIST) built with svg-line-seg. Used for the mode-line, header-line and tab-bar.
  • wrap — a flow of (LABEL . STATE) items wrapped across rows, with per-item "current"/"modified" highlighting. Used for tab lines.

Interactive indicators

Build a clickable segment with svg-line-seg:

(svg-line-seg "buffer.el"
  :id 'ml-buffer                     ; unique hover/identity key
  :help "buffer: buffer.el"
  :action #'switch-to-buffer         ; left/middle click
  :action-help "switch buffer"       ; the "click to …" hint
  :menu '(("Save" . save-buffer)     ; right click
          ("Kill" . kill-this-buffer)))

For wrap lines, put :id/:help/:action/:action-help/:menu in each item's STATE plist. Enable the hover highlight with svg-line-hover-highlight (it needs show-help-function wired to call svg-line--note-help; see the docstring). Clicks select the window they land in, and the tab-bar's clicks and hover are wired up automatically when a tab-bar line is activated.

From existing mode-line content

Mode-line content you already have — a breadcrumb header line, which-func, a VC indicator — carries keymap/help-echo text properties. Hand the propertized string to svg-line-segs-from-string and each clickable region becomes an interactive segment, so it renders through svg-line with its click and hover intact:

(svg-line-segs-from-string (format-mode-line '(:eval (breadcrumb--header-line))))

Fonts and char-advance

svg-line positions right-aligned content, inline pies/bars, interactive segments and wrap breakpoints using svg-line-char-advance — the assumed pixel width of one character. It can't be measured (librsvg rasterises text with its own font stack), so it's a calibration constant. By default (nil) it is derived from the font size via svg-line-char-advance-ratio (0.6, a typical monospace), which keeps layout aligned across font sizes; pin a number for a specific font if right-aligned or hover content sits slightly off.

License

GPL-3.0. See LICENSE.

About

SVG-rendered tab-bar, tab-line, header-line and mode-line for Emacs

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors