Skip to content

y3owk1n/notifier.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

55 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ”” notifier.nvim

A modern, feature-rich notification system for Neovim that transforms the standard vim.notify experience with beautiful UI, smart grouping, and powerful customization options.

Neovim License

demo-notifier.mp4

✨ Features

  • 🎯 Smart Positioning - Multiple notification groups (corners) with independent management
  • 🎨 Beautiful UI - Virtual text rendering with syntax highlighting and custom formatting
  • ⏱️ Timeout Management - Automatic dismissal with configurable timeouts per notification
  • πŸ”„ ID-based Updates - Update existing notifications instead of creating duplicates
  • πŸ“œ History Viewer - Browse all active notifications in a scrollable floating window
  • 🎭 Custom Formatters - Create your own notification layouts and styling
  • ⚑ High Performance - Debounced rendering and efficient virtual text handling
  • πŸŽ›οΈ Fully Configurable - Every aspect customizable through comprehensive options

πŸ“¦ Installation

Using lazy.nvim

{
  "y3owk1n/notifier.nvim",
  config = function()
    require("notifier").setup({
      -- your configuration here
    })
  end
}

Then in your init.lua:

require("notifier").setup()

πŸš€ Quick Start

-- Basic setup with defaults, these are all you need to get started, unless you want to fine-tune the details
require("notifier").setup({
  animation = {
    enabled = true
  }
})

-- Now use enhanced vim.notify
vim.notify("Hello, World!")
vim.notify("Warning message", vim.log.levels.WARN)
vim.notify("Error occurred", vim.log.levels.ERROR)

-- Or you can try out the demo that we prepared to see what notifier.nvim can do
require("notifier.demo").run_demo()

βš™οΈ Configuration

Default Configuration

require("notifier").setup({
  -- Notification timeout in milliseconds
  default_timeout = 3000,

  -- Debounce time for window resize events
  resize_debounce_ms = 150,

  -- Border style for floating windows
  border = "none", -- "none", "single", "double", "rounded", "solid", "shadow"

  -- Winblend for floating windows
  winblend = 0, -- 0-100 transparency

  -- Padding around notification content
  padding = {
    top = 0,
    right = 0,
    bottom = 0,
    left = 0
  },

  -- Default notification group
  default_group = "bottom-right",

  -- Group positioning configurations
  group_configs = {
    ["top-left"] = {
      anchor = "NW",
      row = function() return 0 end,
      col = function() return 0 end,
      winblend = 0, -- overrides global winblend
    },
    ["top-center"] = {
      anchor = "NW",
      row = function() return 0 end,
      col = function() return vim.o.columns / 2 end,
      center_mode = "horizontal", -- Center horizontally only
    },
    ["top-right"] = {
      anchor = "NE",
      row = function() return 0 end,
      col = function() return vim.o.columns end,
    },
    ["left-center"] = {
      anchor = "NW",
      row = function() return (vim.o.lines - vim.o.cmdheight - (vim.o.laststatus > 0 and 1 or 0)) / 2 end,
      col = function() return 0 end,
      center_mode = "vertical", -- Center vertically only
    },
    ["center"] = {
      anchor = "NW",
      row = function() return (vim.o.lines - vim.o.cmdheight - (vim.o.laststatus > 0 and 1 or 0)) / 2 end,
      col = function() return vim.o.columns / 2 end,
      center_mode = "true", -- Center both horizontally and vertically
    },
    ["right-center"] = {
      anchor = "NE",
      row = function() return (vim.o.lines - vim.o.cmdheight - (vim.o.laststatus > 0 and 1 or 0)) / 2 end,
      col = function() return vim.o.columns end,
      center_mode = "vertical", -- Center vertically only
    },
    ["bottom-left"] = {
      anchor = "SW",
      row = function() return vim.o.lines - vim.o.cmdheight - (vim.o.laststatus > 0 and 1 or 0) end,
      col = function() return 0 end,
    },
    ["bottom-center"] = {
      anchor = "SW",
      row = function() return vim.o.lines - vim.o.cmdheight - (vim.o.laststatus > 0 and 1 or 0) end,
      col = function() return vim.o.columns / 2 end,
      center_mode = "horizontal", -- Center horizontally only
    },
    ["bottom-right"] = {
      anchor = "SE",
      row = function() return vim.o.lines - vim.o.cmdheight - (vim.o.laststatus > 0 and 1 or 0) end,
      col = function() return vim.o.columns end,
    },
  },

  -- Width configuration
  width = {
    min_width = 20, -- Minimum notification width
    max_width = nil, -- Maximum width (nil = auto-calculate)
    preferred_width = 50, -- Preferred width when content fits
    max_width_percentage = 0.4, -- Maximum width as percentage of screen
    adaptive = true, -- Automatically adjust width based on content
    wrap_text = true, -- Enable text wrapping for long lines
    wrap_at_words = true, -- Wrap at word boundaries when possible
  },

  -- Icons for different log levels
  icons = {
    [vim.log.levels.TRACE] = "σ°”š ",
    [vim.log.levels.DEBUG] = "ο†ˆ ",
    [vim.log.levels.INFO] = " ",
    [vim.log.levels.WARN] = " ",
    [vim.log.levels.ERROR] = " ",
  }

  -- Formatters
  notif_formatter = require("notifier").formatters.default_notif,
  notif_history_formatter = require("notifier").formatters.default_history,

  -- Animation
  animation = {
    enabled = false, -- animation is off by default
    fade_in_duration = 300,
    fade_out_duration = 300,
  },
})

Custom Styling Example

require("notifier").setup({
  default_timeout = 5000,
  border = "rounded",
  padding = { top = 1, right = 2, bottom = 1, left = 2 },

  group_configs = {
    ["bottom-right"] = {
      anchor = "SE",
      row = function() return vim.o.lines - 3 end, -- Leave more space from bottom
      col = function() return vim.o.columns - 1 end,
      winblend = 20, -- Semi-transparent
    }
  },

  -- Custom icons
  icons = {
    [vim.log.levels.ERROR] = "βœ— ",
    [vim.log.levels.WARN] = "⚠ ",
    [vim.log.levels.INFO] = "β„Ή ",
    [vim.log.levels.DEBUG] = "πŸ› ",
    [vim.log.levels.TRACE] = "πŸ‘ ",
  }
})

πŸ“– Usage Examples

Basic Notifications

-- Simple notification
vim.notify("Task completed successfully!")

-- With log level
vim.notify("Configuration reloaded", vim.log.levels.INFO)

-- Multi-line message
vim.notify("Build failed:\n- Syntax error on line 42\n- Missing dependency")
notifier-demo-1.mov

Advanced Options with Data and Inline Formatters

-- Notification with custom timeout and icon
vim.notify("Long running task started", vim.log.levels.INFO, {
  timeout = 10000, -- 10 seconds
  icon = "⏳ "
})

-- Target specific group
vim.notify("Debug info", vim.log.levels.DEBUG, {
  group_name = "top-left",
  timeout = 0 -- Always there unless manually dismissed
})

-- Updateable notification with ID
vim.notify("Downloading... 0%", vim.log.levels.INFO, {
  id = "download-progress"
})

-- Update the same notification
vim.notify("Downloading... 50%", vim.log.levels.INFO, {
  id = "download-progress" -- Same ID updates existing
})

vim.notify("Download complete!", vim.log.levels.INFO, {
  id = "download-progress"
})

-- Inline formatter with custom data
vim.notify("πŸ–₯️  Server", vim.log.levels.INFO, {
  id = "server-status",
  _notif_formatter = function(opts)
    local data = opts.notif._notif_formatter_data
    local status_icon = data.online and "🟒" or "πŸ”΄"
    local status_text = data.online and "ONLINE" or "OFFLINE"
    local status_color = data.online and "String" or "ErrorMsg"

    return {
      { display_text = opts.line, hl_group = "NotifierInfo", is_virtual = true },
      { display_text = data.name, hl_group = "Identifier", is_virtual = true },
      { display_text = " " .. status_icon .. " ", hl_group = status_color, is_virtual = true },
      { display_text = status_text, hl_group = status_color, is_virtual = true },
      data.uptime and { display_text = " (up " .. data.uptime .. ")", hl_group = "Comment", is_virtual = true } or nil,
    }
  end,
  _notif_formatter_data = {
    name = "prod-api-01",
    online = true,
    uptime = "2d 5h"
  }
})

-- Update server status with new data
vim.notify("", vim.log.levels.WARN, {
  id = "server-status", -- Same ID updates the notification
  _notif_formatter_data = {
    name = "prod-api-01",
    online = false,
    uptime = nil
  }
})
notifier-demo-2.mov

Custom Highlight Groups

-- Use custom highlight group
vim.notify("Special message", vim.log.levels.INFO, {
  hl_group = "MyCustomHighlight"
})

Define your highlight group:

vim.api.nvim_set_hl(0, "MyCustomHighlight", {
  fg = "#ff6b6b",
  bold = true
})

🎨 Custom Formatters

Create your own notification layouts with powerful formatting options:

Global Custom Formatter

-- Custom formatter function
local function my_formatter(opts)
  local notif = opts.notif
  local line = opts.line
  local config = opts.config

  return {
    { display_text = ">> ", hl_group = "Comment", is_virtual = true },
    { display_text = line, hl_group = notif.hl_group, is_virtual = true },
    { display_text = " <<", hl_group = "Comment", is_virtual = true },
  }
end

require("notifier").setup({
  notif_formatter = my_formatter
})
notifier-demo-3.mov

Inline Custom Formatters with Data

Pass custom data and formatters for specific notifications:

-- Progress bar formatter with custom data
vim.notify("", vim.log.levels.INFO, {
  id = "progress-bar",
  timeout = 10000,
  _notif_formatter = function(opts)
    local data = opts.notif._notif_formatter_data
    local progress = data.progress or 0
    local task = data.task or "Processing"
    local total_width = 20
    local filled = math.floor((progress / 100) * total_width)
    local empty = total_width - filled

    local bar = "β–ˆ" .. string.rep("β–ˆ", filled) .. string.rep("β–‘", empty) .. "β–ˆ"
    local percentage = string.format("%3d%%", progress)

    return {
      { display_text = data.icon or "⏳ ", hl_group = "NotifierInfo", is_virtual = true },
      { display_text = task .. ": ", hl_group = "NotifierInfo", is_virtual = true },
      { display_text = bar, hl_group = progress == 100 and "NotifierInfo" or "Comment", is_virtual = true },
      { display_text = " " .. percentage, hl_group = "NotifierInfo", is_virtual = true },
    }
  end,
  _notif_formatter_data = {
    progress = 45,
    task = "Downloading files",
    icon = "πŸ“₯ "
  }
})

-- Update progress (same ID with new data)
vim.notify("", vim.log.levels.INFO, {
  id = "progress-bar",
  _notif_formatter_data = {
    progress = 75,
    task = "Downloading files",
    icon = "πŸ“₯ "
  }
})

-- Complete
vim.notify("", vim.log.levels.INFO, {
  id = "progress-bar",
  timeout = 3000,
  _notif_formatter_data = {
    progress = 100,
    task = "Download complete",
    icon = "βœ… "
  }
})
notifier-demo-4.mov

Advanced Data-Driven Formatters

-- Git status formatter with rich data
vim.notify("", vim.log.levels.INFO, {
  id = "git-status",
  timeout = 8000,
  _notif_formatter = function(opts)
    local data = opts.notif._notif_formatter_data
    local parts = {}

    -- Title
    table.insert(parts, { display_text = "🌿 Git Status", hl_group = "NotifierInfo", is_virtual = true })

    if data.branch then
      table.insert(parts, { display_text = " on ", hl_group = "Comment", is_virtual = true })
      table.insert(parts, { display_text = data.branch, hl_group = "String", is_virtual = true })
    end

    -- Stats with colors
    if data.added and data.added > 0 then
      table.insert(parts, { display_text = " +" .. data.added, hl_group = "diffAdded", is_virtual = true })
    end

    if data.modified and data.modified > 0 then
      table.insert(parts, { display_text = " ~" .. data.modified, hl_group = "diffChanged", is_virtual = true })
    end

    if data.deleted and data.deleted > 0 then
      table.insert(parts, { display_text = " -" .. data.deleted, hl_group = "diffRemoved", is_virtual = true })
    end

    return parts
  end,
  _notif_formatter_data = {
    branch = "feature/new-ui",
    added = 5,
    modified = 3,
    deleted = 1
  }
})

-- LSP diagnostic summary formatter
vim.notify("", vim.log.levels.WARN, {
  id = "lsp-diagnostics",
  _notif_formatter = function(opts)
    local data = opts.notif._notif_formatter_data
    local parts = {
      { display_text = "πŸ” Diagnostics: ", hl_group = "NotifierInfo", is_virtual = true }
    }

    if data.errors > 0 then
      table.insert(parts, { display_text = " " .. data.errors, hl_group = "DiagnosticError", is_virtual = true })
    end
    if data.warnings > 0 then
      table.insert(parts, { display_text = " " .. data.warnings, hl_group = "DiagnosticWarn", is_virtual = true })
    end
    if data.info > 0 then
      table.insert(parts, { display_text = "β„Ή " .. data.info, hl_group = "DiagnosticInfo", is_virtual = true })
    end
    if data.hints > 0 then
      table.insert(parts, { display_text = " " .. data.hints, hl_group = "DiagnosticHint", is_virtual = true })
    end

    return parts
  end,
  _notif_formatter_data = {
    errors = 2,
    warnings = 5,
    info = 3,
    hints = 1
  }
})

-- Build status with timing information
vim.notify("", vim.log.levels.INFO, {
  id = "build-status",
  _notif_formatter = function(opts)
    local data = opts.notif._notif_formatter_data
    local icon = data.status == "success" and "βœ…" or
                 data.status == "error" and "❌" or "⏳"
    local color = data.status == "success" and "NotifierInfo" or
                  data.status == "error" and "NotifierError" or "NotifierWarn"

    return {
      { display_text = icon .. " Build ", hl_group = color, is_virtual = true },
      { display_text = data.status, hl_group = color, is_virtual = true },
      data.duration and { display_text = " (" .. data.duration .. "s)", hl_group = "Comment", is_virtual = true } or nil,
      data.target and { display_text = " [" .. data.target .. "]", hl_group = "Identifier", is_virtual = true } or nil,
    }
  end,
  _notif_formatter_data = {
    status = "success",
    duration = 2.5,
    target = "release"
  }
})
notifier-demo-5.mov

Real-World Integration Examples

-- Function to update download progress with rich visualization
local function update_download_progress(filename, current, total)
  local progress = math.floor((current / total) * 100)
  local speed = current > 0 and string.format("%.1f MB/s", (current / 1024 / 1024)) or "0 MB/s"

  vim.notify("", vim.log.levels.INFO, {
    id = "download-" .. filename,
    timeout = progress == 100 and 3000 or 15000,
    _notif_formatter = function(opts)
      local data = opts.notif._notif_formatter_data
      local bar_width = 25
      local filled = math.floor((data.progress / 100) * bar_width)
      local bar = string.rep("β–ˆ", filled) .. string.rep("β–’", bar_width - filled)

      return {
        { display_text = "πŸ“ ", hl_group = "Directory", is_virtual = true },
        { display_text = data.filename, hl_group = "NotifierInfo", is_virtual = true },
        { display_text = " [", hl_group = "Comment", is_virtual = true },
        { display_text = bar, hl_group = data.progress == 100 and "String" or "Comment", is_virtual = true },
        { display_text = "] ", hl_group = "Comment", is_virtual = true },
        { display_text = data.progress .. "%", hl_group = "Number", is_virtual = true },
        { display_text = " @ " .. data.speed, hl_group = "Comment", is_virtual = true },
      }
    end,
    _notif_formatter_data = {
      filename = filename,
      progress = progress,
      current = current,
      total = total,
      speed = speed
    }
  })
end

-- Usage
update_download_progress("large-file.zip", 0, 100)      -- 0%
update_download_progress("large-file.zip", 50, 100)     -- 50%
update_download_progress("large-file.zip", 100, 100)    -- 100%
notifier-demo-6.mov

πŸ”§ Commands and Functions

Core Functions

-- Show notification history
require("notifier").show_history()

---Dismiss all active notifications immediately or with animation
---@param opts? boolean|{ animated?: boolean, stagger?: number } Options for dismissal
require("notifier").dismiss_all(opts)

Keybindings Example

-- Add to your init.lua
vim.keymap.set("n", "<leader>nh", function()
  require("notifier").show_history()
end, { desc = "Show notification history" })

vim.keymap.set("n", "<leader>nd", function()
  require("notifier").dismiss_all()
end, { desc = "Dismiss all notifications" })

🎯 Notification Groups

Organize notifications by positioning them in different screen areas:

-- Bottom right (default)
vim.notify("System ready", vim.log.levels.INFO)

-- Top right for less intrusive messages
vim.notify("Background task completed", vim.log.levels.INFO, {
  group_name = "top-right"
})

-- Top left for debug information
vim.notify("Variable value: " .. tostring(value), vim.log.levels.DEBUG, {
  group_name = "top-left"
})

-- Bottom left for status updates
vim.notify("Syncing files...", vim.log.levels.INFO, {
  group_name = "bottom-left",
  id = "sync-status"
})

🎨 Highlight Groups

Customize colors by overriding these highlight groups:

-- Main notification styling
vim.api.nvim_set_hl(0, "NotifierNormal", { bg = "#1a1a1a", fg = "#ffffff" })
vim.api.nvim_set_hl(0, "NotifierBorder", { fg = "#444444" })

-- Level-specific colors
vim.api.nvim_set_hl(0, "NotifierError", { fg = "#ff6b6b", bold = true })
vim.api.nvim_set_hl(0, "NotifierWarn", { fg = "#feca57", bold = true })
vim.api.nvim_set_hl(0, "NotifierInfo", { fg = "#48cae4" })
vim.api.nvim_set_hl(0, "NotifierDebug", { fg = "#a8a8a8" })
vim.api.nvim_set_hl(0, "NotifierTrace", { fg = "#6c757d" })

-- History window styling
vim.api.nvim_set_hl(0, "NotifierHistoryNormal", { bg = "#0d1117" })
vim.api.nvim_set_hl(0, "NotifierHistoryBorder", { fg = "#30363d" })
vim.api.nvim_set_hl(0, "NotifierHistoryTitle", { fg = "#f0f6fc", bold = true })

πŸ“± Integration Examples

LSP Progress Notifications with Rich Data

-- Enhanced LSP progress with inline formatters

---Setup a progress spinner for LSP.
---@return nil
local function setup_progress_spinner_custom()
  local spinner_chars = { "β ‹", "β ™", "β Ή", "β Έ", "β Ό", "β ΄", "β ¦", "β §", "β ‡", "⠏" }
  local last_spinner = 0
  local spinner_idx = 1

  ---@type table<string, uv.uv_timer_t|nil>
  local active_timers = {}

  vim.lsp.handlers["$/progress"] = function(_, result, ctx)
    local client = vim.lsp.get_client_by_id(ctx.client_id)
    if not client or type(result.value) ~= "table" then
      return
    end

    local value = result.value
    local token = result.token
    local is_complete = value.kind == "end"
    local has_percentage = value.percentage ~= nil

    local function render()
      local progress_data = {
        percentage = value.percentage or nil,
        description = value.title or "Loading workspace",
        file_progress = value.message or nil,
      }

      if is_complete then
        progress_data.description = "Done"
        progress_data.file_progress = nil
      end

      local icon
      if is_complete then
        icon = " "
      else
        local now = vim.uv.hrtime()
        if now - last_spinner > 80e6 then
          spinner_idx = (spinner_idx % #spinner_chars) + 1
          last_spinner = now
        end
        icon = spinner_chars[spinner_idx]
      end

      vim.notify("", vim.log.levels.INFO, {
        id = string.format("lsp_progress_%s_%s", client.name, token),
        title = client.name,
        _notif_formatter = function(opts)
          local notif = opts.notif
          local _notif_formatter_data = notif._notif_formatter_data

          if not _notif_formatter_data then
            return {}
          end

          local separator = { display_text = " " }

          local icon_hl = notif.hl_group or opts.log_level_map[notif.level].hl_group

          local percent_text = _notif_formatter_data.percentage
              and string.format("%3d%%", _notif_formatter_data.percentage)
            or nil

          local description_text = _notif_formatter_data.description

          local file_progress_text = _notif_formatter_data.file_progress or nil

          local client_name = client.name

          ---@type Notifier.FormattedNotifOpts[]
          local entries = {}

          if icon then
            table.insert(entries, { display_text = icon, hl_group = icon_hl })
            table.insert(entries, separator)
          end

          if percent_text then
            table.insert(entries, { display_text = percent_text, hl_group = "CmdHistoryIdentifier" })
            table.insert(entries, separator)
          end

          table.insert(entries, { display_text = description_text, hl_group = "Comment" })

          if file_progress_text then
            table.insert(entries, separator)
            table.insert(entries, { display_text = file_progress_text, hl_group = "Removed" })
          end

          if client_name then
            table.insert(entries, separator)
            table.insert(entries, { display_text = client_name, hl_group = "ErrorMsg" })
          end

          return entries
        end,
        _notif_formatter_data = progress_data,
      })
    end

    render()

    if not has_percentage then
      if not is_complete then
        local timer = active_timers[token]
        if not timer or timer:is_closing() then
          timer = vim.uv.new_timer()
          active_timers[token] = timer
        end

        if timer then
          timer:start(0, 150, function()
            vim.schedule(render)
          end)
        end
      else
        local timer = active_timers[token]
        if timer and not timer:is_closing() then
          timer:stop()
          timer:close()
          active_timers[token] = nil
        end
        vim.schedule(render)
      end
    end
  end
end

Git Integration with Data Formatting

-- Git status with rich formatting and data
local function show_git_status(branch, stats)
  vim.notify("", vim.log.levels.INFO, {
    id = "git-status",
    timeout = 8000,
    _notif_formatter = function(opts)
      local data = opts.notif._notif_formatter_data
      local parts = {
        { display_text = "🌿 ", hl_group = "String", is_virtual = true },
        { display_text = data.branch, hl_group = "Identifier", is_virtual = true },
      }

      if data.stats.ahead > 0 then
        table.insert(parts, { display_text = " ↑" .. data.stats.ahead, hl_group = "diffAdded", is_virtual = true })
      end

      if data.stats.behind > 0 then
        table.insert(parts, { display_text = " ↓" .. data.stats.behind, hl_group = "diffRemoved", is_virtual = true })
      end

      if data.stats.modified > 0 then
        table.insert(parts, { display_text = " ~" .. data.stats.modified, hl_group = "diffChanged", is_virtual = true })
      end

      if data.stats.untracked > 0 then
        table.insert(parts, { display_text = " +" .. data.stats.untracked, hl_group = "diffAdded", is_virtual = true })
      end

      return parts
    end,
    _notif_formatter_data = {
      branch = branch,
      stats = stats
    }
  })
end

-- Usage
show_git_status("main", { ahead = 2, behind = 0, modified = 3, untracked = 1 })

-- Test results with detailed breakdown
vim.notify("", vim.log.levels.INFO, {
  id = "test-results",
  timeout = 10000,
  _notif_formatter = function(opts)
    local data = opts.notif._notif_formatter_data
    local icon = data.passed == data.total and "βœ…" or "❌"
    local color = data.passed == data.total and "String" or "ErrorMsg"

    return {
      { display_text = icon .. " Tests: ", hl_group = color, is_virtual = true },
      { display_text = data.passed .. "/" .. data.total, hl_group = color, is_virtual = true },
      { display_text = " passed", hl_group = "Comment", is_virtual = true },
      data.duration and { display_text = " (" .. data.duration .. "ms)", hl_group = "Comment", is_virtual = true } or nil,
      data.coverage and { display_text = " " .. data.coverage .. "% coverage", hl_group = "Number", is_virtual = true } or nil,
    }
  end,
  _notif_formatter_data = {
    passed = 45,
    total = 48,
    duration = 2340,
    coverage = 87.5
  }
})

πŸ“‹ Requirements

  • Neovim 0.10+
  • A terminal that supports Unicode icons (optional, for best experience)

πŸ› Troubleshooting

Common Issues

Placements seems to be off for bottom and center:

  • The defaults are calculated based on cmdheight and laststatus, you can override them as needed
  • Ensure that nothing is changing these values after the plugin is setup
  • If you're having some other plugins or autocomds that will alter these 2 values anytime, you can try setting the following autocmd
local old_laststatus = vim.o.laststatus
local old_cmdheight = vim.o.cmdheight

vim.api.nvim_create_autocmd("OptionSet", {
  callback = function()
    local notifier = require("notifier")

    local new_laststatus = vim.o.laststatus
    local new_cmdheight = vim.o.cmdheight

    if new_laststatus ~= old_laststatus or new_cmdheight ~= old_cmdheight then
      old_laststatus = new_laststatus
      old_cmdheight = new_cmdheight

      -- let the plugin recalculate positions
      notifier._internal.utils.cache_config_group_row_col()
    end
  end,
})

Notifications not showing:

  • Ensure you've called require("notifier").setup()
  • Check that your Neovim version is 0.10+

Icons not displaying:

  • Install a Nerd Font and set it as your terminal font
  • Or customize the icons config with plain text alternatives

Performance issues:

  • Reduce default_timeout for faster cleanup
  • Consider using fewer notification groups

🀝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

About

A modern, feature-rich notification system for Neovim that transforms the standard vim.notify experience with beautiful UI, smart grouping, and powerful customization options.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages