Amansingh-afk/milli.nvim

website github github
startup
stars 283
issues 0
subscribers 1
forks 4
CREATED

UPDATED


milli.nvim

Animated ASCII splash screens for Neovim. Ships with 24 bundled splashes, and lets you drop in your own from any image or GIF. Works with dashboard-nvim, alpha-nvim, snacks.nvim, mini.starter, or raw VimEnter.

demo

Contents

Bundled splashes

Install

lazy.nvim:

{ "amansingh-afk/milli.nvim", lazy = false }

packer.nvim:

use "amansingh-afk/milli.nvim"

Quick start

-- preview any bundled splash in a scratch buffer
:MilliPreview fire

-- or wire into your dashboard
require("milli").dashboard({ splash = "fire", loop = true })

List bundled splash names:

:lua print(vim.inspect(require("milli").list()))

For dashboard-nvim / alpha-nvim / snacks.nvim / mini.starter wiring, see Dashboard integrations.

Using your own splash

Powered by milli - the ASCII engine behind this plugin. ⭐ Star it on GitHub if you find it useful.

The 29 bundled splashes are a starting point. Bring any image or GIF you want - a custom logo, mascot, anything - and it becomes a splash in four steps.

1. Install the CLI (@amansingh-afk/milli):

npm install -g @amansingh-afk/milli

2. Generate frames.lua from any image / GIF:

milli export mycat.gif ./out -t lua -w 60 --no-bg

Useful flags:

  • -w 60 - width in columns; tune to taste
  • --no-bg - drop background color (cleaner on dashboards)
  • -m braille - braille mode for higher-detail line art

3. Copy frames.lua into your Neovim config:

mkdir -p ~/.config/nvim/lua/milli/splashes
cp out/frames.lua ~/.config/nvim/lua/milli/splashes/mycat.lua

Neovim's runtimepath auto-discovers ~/.config/nvim/lua/, so this file becomes a sibling to the plugin's bundled splashes - findable by the same machinery, tab-completable in :MilliPreview.

4. Use it - same API as any bundled splash:

require("milli").dashboard({ splash = "mycat", loop = true })

Preview it first:

:MilliPreview mycat

Custom module path (advanced)

If you don't want to piggyback on the milli.splashes namespace (e.g. you organize splashes under a dotfiles module), drop the file anywhere on runtimepath and reference it by Lua module path:

-- ~/.config/nvim/lua/mydots/splashes/mycat.lua
require("milli").dashboard({ module = "mydots.splashes.mycat", loop = true })

Works with every preset - splash = "name" for bundled/user-local, module = "path.to.mod" for custom namespaces.

Dashboard integrations

Pick your dashboard plugin. Each preset (dashboard, alpha, snacks, starter, vimenter) works identically with bundled or custom splashes.

dashboard-nvim

return {
  "nvimdev/dashboard-nvim",
  event = "VimEnter",
  dependencies = { "amansingh-afk/milli.nvim" },
  opts = function()
    local splash = require("milli").load({ splash = "finger" })
    return {
      theme = "doom",
      config = {
        header = splash.frames[1],         -- seed header with frame 0
        center = {
          { icon = "  ", desc = "Find File", key = "f", action = "Telescope find_files" },
          { icon = "  ", desc = "Quit",      key = "q", action = "qa" },
        },
      },
    }
  end,
  config = function(_, opts)
    require("dashboard").setup(opts)
    require("milli").dashboard({ splash = "finger", loop = true })
  end,
}

alpha-nvim

require("milli").alpha({ splash = "fire", loop = true })

snacks.nvim

return {
  "folke/snacks.nvim",
  priority = 1000,
  lazy = false,
  dependencies = { "amansingh-afk/milli.nvim" },
  opts = function()
    local splash = require("milli").load({ splash = "fire" })
    return {
      dashboard = {
        enabled = true,
        preset = {
          header = table.concat(splash.frames[1], "\n"),
        },
        sections = {
          { section = "header", padding = 1 },
          { section = "keys",   gap = 1, padding = 1 },
          { section = "startup" },
        },
      },
    }
  end,
  config = function(_, opts)
    require("snacks").setup(opts)
    require("milli").snacks({ splash = "fire", loop = true })
  end,
}

preset.header seeds frame 0 of the splash as snacks's default header so milli's anchor-search can locate the buffer position to animate over. The splash name in preset.header and in require("milli").snacks({ splash = ... }) must match.

mini.starter

require("milli").starter({ splash = "fire", loop = true })

No plugin (raw VimEnter)

require("milli").vimenter({ splash = "fire", loop = true })

Previewing

:MilliPreview <name>

Opens a scratch buffer, plays the splash in a loop. q or <Esc> dismisses. Tab-completes against bundled splashes and any you've dropped into ~/.config/nvim/lua/milli/splashes/. Run :MilliPreview with no arg to list what's available.

API

require("milli").play(buf, opts)       -- paint/animate into buf
require("milli").load(opts)            -- return the data table
require("milli").list()                -- array of all discovered splash names

require("milli").dashboard(opts)       -- autocmd preset for dashboard-nvim
require("milli").alpha(opts)           -- alpha-nvim
require("milli").snacks(opts)          -- snacks.nvim
require("milli").starter(opts)         -- mini.starter
require("milli").vimenter(opts)        -- raw VimEnter

opts

{
  splash = "fire",     -- bundled or user-local splash name, OR
  module = "mysplash", -- require path to an external splash module, OR
  data = { ... },      -- the data table directly
  loop = true,         -- repeat forever (default: false - play once)
}

A plain string is sugar for { splash = <string> }. So require("milli").dashboard("fire") works.

Requirements

  • Neovim 0.10+ (extmarks, namespaces)
  • termguicolors enabled (vim.opt.termguicolors = true)

Why extmarks, not ANSI escapes?

Neovim buffers strip ANSI. Colors are applied via extmarks + per-color highlight groups generated on demand. The groups are keyed on quantized fg/bg so a truecolor splash doesn't blow through Neovim's highlight-group cap (E849).

License

MIT.