A Neovim theme manager plugin for easy switching, configuring, and auto-applying colorschemes.
Among La Italia's finest painters, Raphael stood out for his harmony in color — just like your interface should.
-
Theme Picker: Interactive floating window to browse and preview configured or other installed themes.
-
Grouped Themes: Organize themes into groups (e.g., "justice-league", "lantern-corps") with collapse/expand functionality.
Supports nested groups intheme_map(tables inside tables), with per‑group cursor memory. -
Auto-Apply by Filetype & Project:
- Automatically switch themes based on:
- Buffer filetype via
filetype_themes. - Project root via
project_themes(match by absolute path prefix; longest prefix wins).
- Buffer filetype via
- Priority flags:
project_overrides_filetype = true→ project wins when both match.filetype_overrides_project = true→ filetype wins when both match.
- Integrates with oil.nvim (uses
oil.get_current_dir()for project detection).
Auto-applied themes are temporary and do not participate in manual history/undo.
- Automatically switch themes based on:
-
Persistence: Saves manually selected themes across sessions:
current,saved,previousthemesbookmarks,history,usage,sort_mode,collapsedstatequick_slots(0–9 favorites)current_profile(active profile)- when
profile_scoped_state = true, bookmarks & quick_slots are stored per profile scope (with a__globalbucket)
All stored in a single JSON file:stdpath("data") .. "/raphael/state.json".
-
Bookmarks and History: Bookmark favorites and track recent themes.
- Dedicated Bookmarks and Recent sections at the top of the picker.
- Full undo/redo stack with
u,<C-r>,H,J,Tin the picker and:RaphaelUndo/:RaphaelRedocommands.
-
Profiles (work / night / presentation):
- Define multiple theme “profiles” as partial configs:
profiles = { work = { default_theme = "..." }, night = { ... }, ... }current_profile = "work"at startup.
- Switch with
:RaphaelProfile work,:RaphaelProfile night,:RaphaelProfile base(clear profile). - Optionally scope bookmarks and quick slots per profile with
profile_scoped_state = true.
- Define multiple theme “profiles” as partial configs:
-
Quick Favorite Slots (0–9):
- In picker:
m0..m9→ assign current theme to quick slot0..9.0..9→ jump to that slot’s theme in the picker and preview it.
- Stored in state as
quick_slots = { ["1"] = "kanagawa-paper-edo", ... }.
- In picker:
-
Compare with Current:
- In picker:
C→ enter compare mode between:- Base = current active theme.
- Candidate = theme under cursor.
- Move with
j/kto change candidate. - Press
Cagain to flip between base ⇄ candidate in the preview.
- In picker:
-
Preview Palette: Visual color blocks for key highlight groups during selection.
- A top mini-bar of colored blocks representing
Normal,Comment,String, etc.
- A top mini-bar of colored blocks representing
-
Sample Code Preview: Optional right-side floating window showing code samples in multiple languages (Lua, Python, JS, TS, Rust, Go, Ruby, Shell), updating as you move in the picker.
-
Keymaps and Commands: Leader-based shortcuts and user commands for management.
-
Session Support: Integrates with session managers for theme restoration via helpers like
raphael.extras.session. -
Configurable Icons: All icons used in the picker (bookmarks, group arrows, history markers, etc.) are configurable via
opts.icons(see below). -
Configuration Management: Export, import, validate, and manage your configurations with presets:
- Export your current configuration to a file with
:RaphaelConfigExport - Import configurations from files with
:RaphaelConfigImport - Validate your configuration with
:RaphaelConfigValidate - Use predefined presets like "minimal", "full-featured", or "presentation" with
:RaphaelConfigPreset - List available config files with
:RaphaelConfigList
- Export your current configuration to a file with
Install via your preferred plugin manager. Example with lazy.nvim:
-- ~/.config/nvim/lua/plugins/raphael.lua
return {
"mavantgarderc/raphael.nvim",
lazy = false,
priority = 1000,
config = function()
require("raphael").setup({
-- Your config here
})
end,
}Configure in your plugin spec's opts table. Examples below:
local raphael = require("raphael")
return {
"mavantgarderc/raphael.nvim",
keys = {
{ "<leader>tp", raphael.open_picker, desc = "Raphael: Configured themes" },
{
"<leader>t/",
function() raphael.open_picker({ exclude_configured = true }) end,
desc = "Raphael: All other themes",
},
{ "<leader>ta", raphael.toggle_auto, desc = "Raphael: Toggle auto-apply" },
{ "<leader>tR", raphael.refresh_and_reload, desc = "Raphael: Refresh themes" },
{ "<leader>ts", raphael.show_status, desc = "Raphael: Show status" },
},
opts = {
leader = "<leader>t",
mappings = {
picker = "p",
next = ">",
previous = "<",
others = "/",
auto = "a",
refresh = "R",
status = "s",
-- ...
},
theme_aliases = {
["kanagawa-paper-sunset"] = "Kanagawa (Paper SUNSET)",
},
group_aliases = {
dc = "Distant Colors",
emotional_entities = "Emotional Entities",
},
default_theme = "kanagawa-paper-ink",
-- Grouped or flat or nested theme_map
theme_map = {
pantheon = { "justice-league-batman", "justice-league-superman" },
kanagawa = { "kanagawa-paper-ink" },
prism = {
emotional_entities = {
"emotional-entities-entity",
"emotional-entities-umbrax",
},
tmnt = {
"tmnt-raphael",
"tmnt-leonardo",
},
},
},
-- Auto-apply per filetype
filetype_themes = {
lua = "kanagawa-paper-ink",
python = "kanagawa-wave",
-- ...
},
-- indentation spaces before each layer
group_indent = 2,
-- Match by directory prefix (longest prefix wins).
-- Use absolute paths or paths with ~; they’re normalized.
project_themes = {
-- Example:
["~/projects/work"] = "kanagawa-paper-edo",
["~/projects/dc-themes"] = "kanagawa-paper-sunset",
-- e.g. your dotfiles repo:
["~/dotfiles"] = "detox-ink",
},
-- Priority when both a project and a filetype mapping match:
-- project_overrides_filetype = true → project wins
-- filetype_overrides_project = true → filetype wins
project_overrides_filetype = true,
filetype_overrides_project = false,
sample_preview = {
enabled = true,
relative_size = 0.5, -- fraction of picker width (0.1–1.0)
languages = nil, -- or { "lua", "python", "rust" } to restrict
},
-- Sort modes: "alpha" | "recent" | "usage" | custom
sort_mode = "alpha",
custom_sorts = {
-- my_sort = function(a, b) return a < b end
},
history_max_size = 13,
profiles = {
work = {
default_theme = "kanagawa-paper-edot",
filetype_themes = { lua = "kanagawa-paper-edo" },
project_themes = { ["~/clients/bigcorp"] = "detox-ink" },
},
night = {
default_theme = "detox-ink",
filetype_themes = { lua = "detox-ink" },
},
presentation = {
default_theme = "kanagawa-paper-obsidian",
},
},
workspace_profiles = {
["~/projects/work"] = "work",
["~/slides"] = "presentation",
},
-- Active profile at startup (or nil for base config)
current_profile = "work",
-- If true, bookmarks & quick_slots are stored per-profile (with a __global fallback)
profile_scoped_state = false,
icons = {
-- sections
-- HEADER = "🎨 Colorschemes",
-- RECENT_HEADER = "⏱ Recent",
-- BOOKMARKS_HEADER = "★ Bookmarks",
-- markers
-- BOOKMARK = "★ ",
-- CURRENT_ON = "● ",
-- CURRENT_OFF = "○ ",
-- WARN = "⚠ ",
-- groups
-- GROUP_EXPANDED = "▾ ",
-- GROUP_COLLAPSED = "▸ ",
},
-- on_apply hook (e.g. refresh lualine)
on_apply = function(theme)
vim.schedule(function()
local ok, lualine = pcall(require, "lualine")
if ok then
local cfg = lualine.get_config()
cfg.options = cfg.options or {}
cfg.options.theme = "auto"
lualine.setup(cfg)
end
end)
end,
enable_autocmds = true,
enable_commands = true,
enable_keymaps = true,
enable_picker = true,
},
}<leader>tp: Open picker for configured themes<leader>t/: Open picker for other installed themes<leader>ta: Toggle auto-apply<leader>tR: Refresh themes and reload current<leader>ts: Show status<leader>t</<leader>t>: Previous / next theme (viamappings.previous/mappings.next)
<CR>: Select themeq/<Esc>: Quit (revert theme)/: Search themesa: Clear search / show all themesb: Toggle bookmarkc: Collapse/expand groups: Cycle sort modeS: Toggle sorting on/offR: Toggle reverse sorting (descending)
j/k: Navigate (wraps around)<C-j>/<C-k>: Jump to next/prev group header (wraps)<C-l>/<C-h>: Jump into / out of group (header/child)[g/]g: Jump to prev/next group header (wraps)gg/G: Go to top / bottom<C-u>/<C-d>: Half-page up/downzt/zz/zb: Scroll current line to top/center/bottomga: Jump to first theme (All)gb: Jump to Bookmarks sectiongr: Jump to Recent section[b/]b: Jump to prev/next bookmark (skips Bookmark group section)[r/]r: Jump to prev/next history state (skips Recent group section)
u: Undo theme change<C-r>: Redo theme changeH: Show full historyJ: Jump to a history positionT: Show quick history statsr: Apply random theme
i: Show code sample preview / iterate languages forwardI: Iterate languages backwardC: Compare candidate with current theme in preview:- First
C: base = current active theme, candidate = line under cursor. - Move with
j/kto change candidate. - Further
C: toggle between showing base ⇄ candidate.
- First
m0..m9: Assign current theme to quick slot 0..9.0..9: Jump to that slot’s theme in the picker and preview it.
?: Show this help.
-
:RaphaelPickerOpen picker (configured themes). -
:RaphaelPickerAllOpen picker (all except configured). -
:RaphaelApply {theme}Apply a theme by name (supports aliases). -
:RaphaelToggleAutoToggle auto-apply by filetype/project. -
:RaphaelRefreshRefresh theme list and reload current. -
:RaphaelStatusShow current theme status (includes profile name if any). -
:RaphaelHelpShow Raphael help. -
:RaphaelHistoryShow full theme history. -
:RaphaelUndo/:RaphaelRedoUndo / redo last theme change. -
:RaphaelRandomApply a random theme. -
:RaphaelBookmarkToggleToggle bookmark for the theme under the cursor (opens picker if needed). -
:RaphaelProfile [name]Manage profiles::RaphaelProfile→ list all profiles and mark current with*.:RaphaelProfile work/night/presentation→ switch to that profile.:RaphaelProfile base→ clear profile (use base config only).
-
:RaphaelConfigExport [file_path]Export current configuration to a JSON file (defaults to~/.config/nvim/raphael/configs/exported_config.json). -
:RaphaelConfigImport file_pathImport and apply configuration from a JSON file. -
:RaphaelConfigValidateValidate current configuration and show diagnostics. -
:RaphaelConfigListList available configuration files in the raphael configs directory. -
:RaphaelConfigPreset [preset_name]Apply a configuration preset::RaphaelConfigPreset→ list all available presets.:RaphaelConfigPreset minimal/full-featured/presentation→ apply that preset.
:lua require("raphael.picker.ui").toggle_debug()
:lua print(vim.inspect(require("raphael.picker.ui").get_cache_stats()))