Repository: sunjon/Shade.nvim
Branch: master
Commit: 4286b5abc47d
Files: 3
Total size: 14.0 KB
Directory structure:
gitextract_wrvkyd1r/
├── LICENSE
├── README.md
└── lua/
└── shade.lua
================================================
FILE CONTENTS
================================================
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Senghan Bright
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# shade.nvim
Shade is a Neovim plugin that dims your inactive windows, making it easier to see the active window at a glance.
<img src="https://raw.githubusercontent.com/sunjon/images/master/shade_demo.gif" alt="screenshot" width="800"/>
## Installation
### [Packer](https://github.com/wbthomason/packer.nvim)
```
use 'sunjon/shade.nvim'
```
### [Vim-Plug](https://github.com/junegunn/vim-plug)
```
Plug 'sunjon/shade.nvim'
```
## Configuration
```
require'shade'.setup({
overlay_opacity = 50,
opacity_step = 1,
keys = {
brightness_up = '<C-Up>',
brightness_down = '<C-Down>',
toggle = '<Leader>s',
}
})
```
* The `keys` table above shows available actions. No mappings are defined by default.
* The color of the numbers in the brightness control popup can be customized by creating a highlight group named: `ShadeBrightnessPopup` and setting the attributes to your liking.
## License
Copyright (c) Senghan Bright. Distributed under the MIT license
================================================
FILE: lua/shade.lua
================================================
-- TODO: remove all active_overlays on tab change
local api = vim.api
local E = {}
E.DEFAULT_OVERLAY_OPACITY = 50
E.DEFAULT_OPACITY_STEP = 1
E.DEBUG_OVERLAY_OPACITY = 90
E.NOTIFICATION_TIMEOUT = 1000 -- ms
local state = {}
state.active = false
state.active_overlays = {}
state.shade_nsid = nil
state.notification_timer = nil
state.notification_window = nil
-- TODO: log to file and/or nvim_echo
local function log(event, msg)
if state.debug == false then
return
end
msg = tostring(msg)
local info = debug.getinfo(2, "Sl")
local line_info = "[shade:" .. info.currentline .. "]"
local timestamp = ("%s %-15s"):format(os.date("%H:%M:%S"), line_info)
local event_msg = ("%-10s %s"):format(event, msg)
print(timestamp .. " : " .. event_msg)
end
--
local font = {
0x7C, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0x7C, 0x00, -- (0)
0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, -- (1)
0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00, -- (2)
0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x70, 0x00, -- (3)
0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, -- (4)
0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, -- (5)
0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, -- (6)
0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, -- (7)
0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, -- (8)
0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, -- (9)
}
local function get_digit(number, pos)
local n = 10 ^ pos
local n1 = 10 ^ (pos - 1)
return math.floor((number % n) / n1)
end
local function digitize(number)
assert(type(number) == "number")
local len = math.floor(math.log10(number) + 1)
local block_chars = {[0] = " ", [1] = "▀", [2] = "▄", [3] = "█"}
-- generate bit table
local offset, char, row_bits, hex_val, b
local characters = {}
for n = 1, len do
offset = get_digit(number, len - n + 1) * 8
char = {}
for row = 1, 8 do
row_bits = {}
hex_val = font[offset + row]
for i = 1, 8 do
b = bit.band(bit.rshift(hex_val, 8 - i), 1)
row_bits[i] = b
end
table.insert(char, row_bits)
end
table.insert(characters, char)
end
-- generate strings
local output = {}
local upper, lower, combined, row_str
for row = 1, 8, 2 do
row_str = " "
for _, c in ipairs(characters) do
for col = 1, 8 do
upper = c[row][col]
lower = c[row + 1][col] * 2
combined = block_chars[upper + lower]
row_str = row_str .. combined
end
end
row_str = row_str .. " "
table.insert(output, row_str)
end
return output
end
--
local function filter_wininfo(wininfo)
return {
relative = "editor",
style = "minimal",
focusable = false,
row = wininfo.winrow - 1,
col = wininfo.wincol - 1,
width = wininfo.width,
height = wininfo.height,
}
end
--
local function create_hl_groups()
local overlay_color
if state.debug == true then
overlay_color = "#77a992"
else
overlay_color = "None"
end
api.nvim_command("highlight ShadeOverlay gui='nocombine' guibg=" .. overlay_color)
-- Link to default hl_group if not user defined
local exists, _ = pcall(function()
return vim.api.nvim_get_hl_by_name("ShadeBrightnessPopup", false)
end)
if not exists then
api.nvim_command("highlight link ShadeBrightnessPopup Number")
end
end
--
local function create_floatwin(config)
local window = {}
window.wincfg = config
window.bufid = api.nvim_create_buf(false, true)
window.winid = api.nvim_open_win(window.bufid, false, config)
return window
end
--
local function map_key(mode, key, action)
local req_module = ("<cmd>lua require'shade'.%s<CR>"):format(action)
vim.api.nvim_set_keymap(mode, key, req_module, {noremap = true, silent = true})
end
local function shade_window(winid)
local overlay = state.active_overlays[winid]
if overlay then
if api.nvim_win_is_valid(overlay.winid) then
api.nvim_win_set_option(overlay.winid, "winblend", state.overlay_opacity)
log("shade_window",
("[%d] : overlay %d ON (winblend: %d)"):format(winid, overlay.winid, state.overlay_opacity))
end
else
log("shade_window", "overlay not found for " .. winid)
end
end
local function unshade_window(winid)
local overlay = state.active_overlays[winid]
if overlay then
if api.nvim_win_is_valid(overlay.winid) then
api.nvim_win_set_option(overlay.winid, "winblend", 100)
log("unshade_window",
("[%d] : overlay %d OFF (winblend: 100 [disabled])"):format(winid, overlay.winid))
end
else
log("unshade_window", "overlay not found for " .. winid)
end
end
-- shade everything on a tabpage except current window
local function shade_tabpage(winid)
winid = winid or api.nvim_get_current_win()
for overlay_winid, _ in pairs(state.active_overlays) do
local diff_enabled = api.nvim_win_get_option(overlay_winid, 'diff')
if overlay_winid ~= winid and diff_enabled == false then
log("deactivating window", overlay_winid)
shade_window(overlay_winid)
end
end
end
--
local function remove_all_overlays()
for _, overlay in pairs(state.active_overlays) do
api.nvim_win_close(overlay.winid, true)
end
state.active_overlays = {}
end
local function create_overlay_window(winid, config)
local new_window = create_floatwin(config)
state.active_overlays[winid] = new_window
api.nvim_win_set_option(new_window.winid, "winhighlight", "Normal:ShadeOverlay")
api.nvim_win_set_option(new_window.winid, "winblend", state.overlay_opacity)
log("create overlay",
("[%d] : overlay %d created"):format(winid, state.active_overlays[winid].winid))
end
--
local function create_tabpage_overlays(tabid)
local wincfg
for _, winid in pairs(api.nvim_tabpage_list_wins(tabid)) do
wincfg = api.nvim_call_function("getwininfo", {winid})[1]
create_overlay_window(winid, filter_wininfo(wincfg))
end
unshade_window(api.nvim_get_current_win())
end
local shade = {}
-- init
shade.init = function(opts)
state.active_overlays = {}
opts = opts or {}
state.debug = opts.debug or false
state.overlay_opacity = opts.overlay_opacity or
(state.debug == true and E.DEBUG_OVERLAY_OPACITY or
E.DEFAULT_OVERLAY_OPACITY)
state.opacity_step = opts.opacity_step or E.DEFAULT_OPACITY_STEP
state.shade_under_float = opts.shade_under_float or true
state.shade_nsid = api.nvim_create_namespace("shade")
local shade_action = {
["brightness_up"] = "brightness_up()",
["brightness_down"] = "brightness_down()",
["toggle"] = "toggle()",
}
if opts.keys ~= nil then
for action, key in pairs(opts.keys) do
if not shade_action[action] then
log("init:keymap", "unknown action " .. action)
else
map_key("n", key, shade_action[action])
end
end
end
create_hl_groups()
api.nvim_set_decoration_provider(state.shade_nsid, {on_win = shade.event_listener})
-- setup autocommands -- TODO: set a precalculated winid
api.nvim_exec([[
augroup shade
au!
au WinEnter,VimEnter * call v:lua.require'shade'.autocmd('WinEnter', win_getid())
au WinClosed * call v:lua.require'shade'.autocmd('WinClosed', expand('<afile>'))
au TabEnter * call v:lua.require'shade'.autocmd('TabEnter', win_getid())
au OptionSet diff call v:lua.require'shade'.autocmd('OptionSet', win_getid())
augroup END
]], false)
log("Init", "-- Shade.nvim started --")
return true
end
--
shade.on_win_enter = function(event, winid)
log(event, winid)
if not state.active_overlays[winid] then
local float_cfg = api.nvim_win_get_config(winid)
if float_cfg["relative"] == "" then
local wincfg = api.nvim_call_function("getwininfo", {winid})[1]
create_overlay_window(winid, filter_wininfo(wincfg))
else
log(event, "floating window ignored: " .. winid)
if not state.shade_under_float then
return
end
end
end
-- hide the overlay on entered window
unshade_window(winid)
-- place overlays on all other windows
shade_tabpage(winid)
end
shade.event_listener = function(_, winid, _, _, _)
local cached = state.active_overlays[winid]
if not cached then
return
end
-- check if window dims match cache
local current_wincfg = vim.api.nvim_call_function("getwininfo", {winid})[1]
local resize_metrics = {"width", "height", "wincol", "winrow"}
for _, m in pairs(resize_metrics) do
if current_wincfg[m] ~= cached.wincfg[m] then
log("event_listener: resized", winid)
state.active_overlays[winid].wincfg = current_wincfg
api.nvim_win_set_config(cached.winid, filter_wininfo(current_wincfg))
goto continue
end
end
::continue::
end
--
-- destroy overlay window on WinClosed
shade.on_win_closed = function(event, winid)
winid = tonumber(winid) -- TODO: when did winid become a string?
local overlay = state.active_overlays[winid]
if overlay == nil then
log(event, "no overlay to close")
else
api.nvim_win_close(overlay.winid, false)
log(event, ("[%d] : overlay %d destroyed"):format(winid, overlay.winid))
state.active_overlays[winid] = nil
end
end
shade.change_brightness = function(level)
local curr_winid = api.nvim_get_current_win()
state.overlay_opacity = level
for id, w in pairs(state.active_overlays) do
if id ~= curr_winid then
log("winblend: winid" .. w.winid, level)
api.nvim_win_set_option(w.winid, "winblend", level)
end
end
local status_opts = {
relative = "editor",
style = "minimal",
focusable = false,
row = 1,
col = vim.o.columns - 18,
width = 18,
height = 4,
}
if state.notification_window == nil then
state.notification_window = create_floatwin(status_opts)
api.nvim_win_set_option(state.notification_window.winid, "winhighlight",
"Normal:ShadeBrightnessPopup")
api.nvim_win_set_option(state.notification_window.winid, "winblend", 10)
log("notification", "popup created")
end
local output_lines = digitize(level)
api.nvim_buf_set_lines(state.notification_window.bufid, 0, 7, false, output_lines)
if state.notification_timer ~= nil then
state.notification_timer:stop()
state.notification_timer = nil
log("notification", "timer aborted")
end
state.notification_timer = vim.loop.new_timer()
state.notification_timer:start(E.NOTIFICATION_TIMEOUT, 0, vim.schedule_wrap(
function()
if api.nvim_win_is_valid(state.notification_window.winid) then
api.nvim_win_close(state.notification_window.winid, true)
state.notification_window = nil
log("notification", "timer ended")
log("notification", "popup closed")
end
end))
end
-- Main
local M = {}
M.setup = function(opts)
if state.active == true then
return
end
shade.init(opts)
state.active = true
end
M.brightness_up = function()
if not state.active then
return
end
local adjusted = state.overlay_opacity + state.opacity_step
if adjusted > 99 then
adjusted = 99
end
shade.change_brightness(adjusted)
end
M.brightness_down = function()
if not state.active then
return
end
local adjusted = state.overlay_opacity - state.opacity_step
if adjusted < 0 then
adjusted = 0
end
shade.change_brightness(adjusted)
end
M.toggle = function()
if state.active then
remove_all_overlays()
print("off")
state.active = false
else
create_tabpage_overlays(0)
print("on")
state.active = true
end
end
M.autocmd = function(event, winid)
if not state.active then
return
end
log("AutoCmd: " .. event .. " : " .. winid)
local event_fn = {
["WinEnter"] = function()
shade.on_win_enter(event, winid)
end,
["WinClosed"] = function()
shade.on_win_closed(event, winid)
end,
["TabEnter"] = function()
remove_all_overlays()
create_tabpage_overlays(0)
end,
["OptionSet"] = function()
local diff_enabled = api.nvim_get_vvar('option_new')
if diff_enabled then
unshade_window(winid)
shade_tabpage(winid)
end
end
}
local fn = event_fn[event]
if fn then
fn()
end
end
return M
gitextract_wrvkyd1r/
├── LICENSE
├── README.md
└── lua/
└── shade.lua
Condensed preview — 3 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (15K chars).
[
{
"path": "LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2021 Senghan Bright\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "README.md",
"chars": 997,
"preview": "# shade.nvim\n\nShade is a Neovim plugin that dims your inactive windows, making it easier to see the active window at a g"
},
{
"path": "lua/shade.lua",
"chars": 12317,
"preview": "-- TODO: remove all active_overlays on tab change\nlocal api = vim.api\n\nlocal E = {}\nE.DEFAULT_OVERLAY_OPACITY = 50\nE.DEF"
}
]
About this extraction
This page contains the full source code of the sunjon/Shade.nvim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 3 files (14.0 KB), approximately 4.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.