• Emacs Lisp 94.4%
  • Makefile 5.6%
Find a file
Jotham Lim 7090fdc3a0
persp-mode-tab-bar 0.2.1: leave the tab bar the way others left it
A review round, mostly about sharing the tab bar. Turning the mode off now
keeps what other packages did to `tab-bar-format' while it was on. With
persp-mode off the bar draws the real tabs rather than nothing, and the README
recipe follows persp-mode both ways. The "+" button is replaced along with the
tabs. `unload-feature' turns the mode off first. And Package-Requires names
persp-mode 3.0.8, the first release with the hook the bar redraws on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:03:31 +08:00
.github/workflows ci: add snapshot, the oldest persp-mode and melpazoid 2026-09-28 18:01:54 +08:00
images docs: say how the two faces default and how to take them over 2026-09-24 12:19:04 +08:00
test turn the mode off when the feature is unloaded 2026-09-28 18:00:09 +08:00
.gitignore ci: add snapshot, the oldest persp-mode and melpazoid 2026-09-28 18:01:54 +08:00
CHANGELOG.md persp-mode-tab-bar 0.2.1: leave the tab bar the way others left it 2026-09-28 18:03:31 +08:00
LICENSE meet MELPA's two hard requirements ahead of submitting 2026-09-24 22:08:21 +08:00
Makefile ci: add snapshot, the oldest persp-mode and melpazoid 2026-09-28 18:01:54 +08:00
persp-mode-tab-bar.el persp-mode-tab-bar 0.2.1: leave the tab bar the way others left it 2026-09-28 18:03:31 +08:00
README.md docs: say who it's for, what else there is, and what Doom does with the bar 2026-09-28 18:03:02 +08:00

persp-mode-tab-bar

CI License: GPL v3+

A tab bar for persp-mode workspaces. Works with Doom Emacs's +workspace commands and with plain persp-mode.

It's for people already on persp-mode, most of all through Doom's :ui workspaces. persp-mode only tells you which workspace you're in by echoing the list when you switch, and I wanted to see it all the time, so this puts the list in the tab bar.

Switching workspaces: the tab bar follows

Install

Not on MELPA yet. With use-package and Emacs 30's :vc:

(use-package persp-mode-tab-bar
  :vc (:url "https://github.com/Jotham-LEC/persp-mode-tab-bar" :rev :newest)
  :hook (persp-mode . (lambda () (persp-mode-tab-bar-mode (if persp-mode 1 -1)))))

Or with straight.el:

(straight-use-package
 '(persp-mode-tab-bar :type git :host github :repo "Jotham-LEC/persp-mode-tab-bar"))

Or Doom, in packages.el:

(package! persp-mode-tab-bar
  :recipe (:host github :repo "Jotham-LEC/persp-mode-tab-bar"))

Emacs 29.1 or newer, and persp-mode 3.0.8 or newer.

Use

(persp-mode-tab-bar-mode 1)

In Doom, where :ui workspaces gives you persp-mode already, the mode detects the +workspace commands and uses them, so the numbering and the order match what SPC TAB . displays. It also silences Doom's echoed workspace list, since the bar is now saying the same thing permanently. Set persp-mode-tab-bar-silence-doom-echo to nil if you want both. Doom's :ui tabs is centaur-tabs, which tabs buffers — the two do not overlap.

One Doom side effect: :ui workspaces hangs its tab-bar integration off tab-bar-mode-hook (modules/ui/workspaces/config.el), so turning on the tab bar, which this mode does, also turns on Doom saving each workspace's real tabs when you leave it and restoring them when you come back, and writing them to the session file. It's harmless, since the tabs aren't drawn, but it's why a workspace can come back with tabs you can't see.

Customising

Face Default Draws
persp-mode-tab-bar-current bold + highlight the workspace you are in
persp-mode-tab-bar-inactive shadow + tab-bar-tab-inactive every other workspace

Both defaults are compositions of stock faces, so the list follows whatever theme you load. To override, set them the way you set any other face — custom-set-faces, or :custom-face in a use-package block, or M-x customize-face:

(custom-set-faces
 '(persp-mode-tab-bar-current
   ((t :inherit bold :foreground "#ffffff" :background "#6f5bd5")))
 '(persp-mode-tab-bar-inactive
   ((t :foreground "#8f8f9d"))))

(setq tab-bar-separator "  ")

A restyled workspace list

The spacing around a name is the tab bar's, not this package's: tab-bar-separator sets what goes between the items, and on a GUI frame a :box on either face pads and outlines them.

Variable Default Does
persp-mode-tab-bar-backend auto pins the workspace API — doom or persp-mode — instead of detecting it
persp-mode-tab-bar-replace the tab items and the + which tab-bar-format items the workspace list stands in for
persp-mode-tab-bar-silence-doom-echo t drops Doom's echoed workspace list, which the bar is now showing permanently

Compatibility

The mode contributes one tab-bar-format item rather than taking the bar. It splices the workspace list in where the real tabs were and leaves every other item where it found it, so history buttons, tab-bar-format-global and packages like tab-bar-echo-area or tab-bar-notch go on working; turning the mode off puts the format back, and tab-bar-show with it, keeping whatever other packages added or removed while it was on. Its own two faces leave themes and vim-tab-bar alone, and its workspace-N item keys collide with none of Emacs's. Real tab-bar tabs, Doom's per-workspace tab sets included, keep working — they are simply not drawn.

One thing it can't fix: right-clicking a workspace brings up the tab bar's own context menu, with "New tab" and "Reopen closed tab" in it, and both make a real tab you then can't see. That menu is bound once for the whole bar in tab-bar-map and decides what to offer from the item's key, so there is no way to give these items a menu of their own.

Alternatives

If you're not on persp-mode already, one of these probably suits you better.

  • tab-bar with project.el, both built in. Each tab is a window layout, and C-x t p opens a project in a new tab. Every tab sees every buffer, though.
  • tabspaces builds workspaces on tab-bar and project.el, with a buffer list per tab.
  • perspective.el is a different package from persp-mode, with the same idea of per-workspace buffer lists.
  • activities.el and burly save and restore window layouts and their buffers, rather than keeping workspaces alive; activities.el can give each one a tab.
  • bufler groups buffers by rules (project, mode, directory) and makes workspaces out of the groups.

Contributing

It is a small package and a personal one. Bug reports and pull requests are welcome.

make deps                # persp-mode and package-lint, into ./.deps
make compile lint test   # byte-compile clean, checkdoc, package-lint, ERT

CI runs the same on Emacs 29, 30 and snapshot, runs the tests against persp-mode 3.0.8, the oldest it supports, and runs melpazoid. To test against a persp-mode checkout of your own:

make compile test PERSP_MODE_DIR=../persp-mode.el

License

GPL-3.0-or-later.