Skip to content

lalitmee/codecompanion-spinners.nvim

Repository files navigation

CodeCompanion Spinners Banner

⏳ codecompanion-spinners.nvim

Beautiful, configurable status spinners for codeCompanion.nvim


A companion extension for codecompanion.nvim that provides beautiful, configurable status spinners and notifications to give you real-time feedback on the AI's activity.


📹 Demo

snacks.mp4

✨ Features

  • 🎨 Multiple Spinner Styles: Choose from several built-in spinner styles to suit your workflow.
  • ⚙️ Highly Configurable: Customize icons, messages, highlight groups, and spinner behavior.
  • 🧠 State Aware: Spinners dynamically change to reflect the AI's state (e.g., Thinking, Running Tools, Awaiting Review).
  • 🔌 Extensible: Built as a proper codecompanion.nvim extension.

📦 Installation

Install with your favorite plugin manager. This plugin is a codecompanion.nvim extension and should be configured within its extensions table.

⚠️ Note: This is an extension for codecompanion.nvim. Make sure you have the main plugin installed first.

{
  "olimorris/codecompanion.nvim",
  dependencies = {
    "nvim-lua/plenary.nvim",
    "nvim-telescope/telescope.nvim",
     "lalitmee/codecompanion-spinners.nvim", -- Install the spinners extension
      -- 📦 Optional dependencies for certain spinner styles:
      -- "j-hui/fidget.nvim",
      -- "folke/snacks.nvim",
      -- "folke/noice.nvim",
      -- "nvim-lualine/lualine.nvim",
      -- "rebelot/heirline.nvim",
  },
  opts = {
    -- ... your main codecompanion config ...
     extensions = {
       spinner = {
         -- enabled = true, -- This is the default
         opts = {
           -- Your spinner configuration goes here
            style = "cursor-relative",
         },
       },
     },
  },
}

⚙️ Configuration

You can configure the extension by passing a table to the opts key under extensions.spinner.

⚙️ Default Configuration

Here is the complete default configuration. You only need to specify the values you wish to override.

{
	-- The spinner style to use.
	-- Available options: "cursor-relative", "snacks", "fidget", "noice", "lualine", "heirline", "native", "none"
	style = "cursor-relative",

	-- Default icon to show when spinners are idle (default: "")
	default_icon = "",

 	-- A table mapping internal states to their display content (icons and messages).
  content = {
    -- 🧠 General states
    thinking = { icon = "", message = "Thinking...", spacing = "  " },
    receiving = { icon = "", message = "Receiving...", spacing = "  " },
    done = { icon = "", message = "Done!", spacing = "  " },
    stopped = { icon = "", message = "Stopped", spacing = "  " },
    cleared = { icon = "", message = "Chat cleared", spacing = "  " },

    -- 🔧 Tool-related states
    tools_started = { icon = "", message = "Running tools...", spacing = "  " },
    tools_finished = { icon = "", message = "Processing tool output...", spacing = "  " },

    -- 📝 Diff-related states
    diff_attached = { icon = "", message = "Review changes", spacing = "  " },
    diff_accepted = { icon = "", message = "Change accepted", spacing = "  " },
    diff_rejected = { icon = "", message = "Change rejected", spacing = "  " },

    -- 💬 Chat-related states (primarily for notifiers like snacks)
    chat_ready = { icon = "", message = "Chat ready", spacing = "  " },
    chat_opened = { icon = "", message = "Chat opened", spacing = "  " },
    chat_hidden = { icon = "", message = "Chat hidden", spacing = "  " },
    chat_closed = { icon = "", message = "Chat closed", spacing = "  " },
  },

 	-- 🎨 Spinner-specific settings
  ["cursor-relative"] = {
    text = "",
    -- text = "",
    hl_positions = {
      { 0, 3 }, -- First circle
      { 3, 6 }, -- Second circle
      { 6, 9 }, -- Third circle
    },
    interval = 100,
    hl_group = "Title",
    hl_dim_group = "NonText",
  },
  fidget = {
    -- No specific options for now
  },
   snacks = {
     -- No specific options for now
   },
   lualine = {
     -- No specific options for now
   },
   heirline = {
     -- No specific options for now
   },
   native = {
    done_timer = 500,
    window = {
      relative = "editor",
      width = 30,
      height = 1,
      row = vim.o.lines - 5,
      col = vim.o.columns - 35,
      style = "minimal",
      border = "rounded",
      title = "CodeCompanion",
      title_pos = "center",
      focusable = false,
      noautocmd = true,
    },
    win_options = {
      -- winblend = 10,
    },
  },
}

🎨 Available Spinners

You can choose one of the following styles by setting the style option:

Quick Overview

  • 🖱️ cursor-relative (Default) - Floating window spinner near cursor
  • 📊 fidget - Progress notifications via fidget.nvim
  • 🍿 snacks - Rich notifications via snacks.nvim or vim.notify
  • 📣 noice - Rich notifications via noice.nvim
  • 📏 lualine - Statusline component for lualine.nvim
  • 🎨 heirline - Statusline component for heirline.nvim
  • 🪟 native - Highly configurable floating window
  • 🚫 none - Disable all spinners

Detailed Descriptions

🖱️ cursor-relative (Default)

A minimal, animated spinner that floats in a small window next to your cursor. It provides unobtrusive feedback without getting in your way.

Demo:

cursor-relative.mp4

Visuals:

  • Active: A smooth highlight animation across the spinner characters (e.g.,  with moving highlights).
  • Finished: The spinner is replaced by a checkmark and a "Done!" message, which persists for a short duration before fading away.

🔧 Specific Options:

opts = {
  style = "cursor-relative",
  ["cursor-relative"] = {
    -- The spinner text characters
    text = "",
    -- Alternative: text = "",

    -- Highlight positions for animation (start_col, end_col pairs)
    hl_positions = {
      { 0, 3 }, -- First circle
      { 3, 6 }, -- Second circle
      { 6, 9 }, -- Third circle
    },

    -- Animation interval in milliseconds
    interval = 100,

    -- Highlight groups
    hl_group = "Title",        -- Active highlight
    hl_dim_group = "NonText",  -- Dimmed background
  },
}

📊 fidget

This style leverages the excellent fidget.nvim plugin to display progress in the corner of your screen. It's a great choice if you already use fidget.nvim for LSP progress.

📋 Prerequisites: You must have j-hui/fidget.nvim installed.

Demo:

fidget.mp4

Visuals:

  • 📋 A "CodeCompanion" task will appear in your fidget window, showing the current status (e.g., "Thinking...").
  • ✅ When complete, the task is marked as finished.
  • 🎯 This spinner is intentionally simple and only notifies for the thinking and done states to avoid excessive notifications.

Specific Options: This spinner currently has no specific options.

opts = {
  style = "fidget",
}

🍿 snacks

This style uses snacks.nvim (or the built-in vim.notify as a fallback) to show rich, animated notifications. It provides the most detailed feedback of all the spinners.

📋 Prerequisites: For the best experience, you should have folke/snacks.nvim installed.

Demo:

snacks.mp4

Visuals:

  • 🔔 A notification popup appears with an icon and status message in the format <icon> <message> (e.g., "⚛ Thinking...").
  • 🔄 The notification updates in-place as the AI moves through different states with animated spinner icons.
  • 📢 It also shows one-off notifications for events like accepting/rejecting diffs or opening/closing the chat window.

Specific Options: This spinner currently has no specific options.

opts = {
  style = "snacks",
}

📏 lualine

This style integrates with lualine.nvim to show spinner status directly in your statusline. It provides seamless integration with your existing lualine setup.

📋 Prerequisites: You must have nvim-lualine/lualine.nvim installed and configured.

Demo:

lualine.mp4

Visuals:

  • 📊 Shows animated spinner with icons and text messages in your lualine status bar
  • 🔄 Updates smoothly with lualine's refresh cycle
  • 📝 Displays messages like "🤖 Thinking...", "📨 Receiving...", "✅ Done!"
  • 🎯 Only shows when there's active AI activity

Specific Options: This spinner currently has no specific options.

opts = {
  style = "lualine",
}

-- In your lualine config:
require('lualine').setup({
  sections = {
    lualine_c = {
      require('codecompanion._extensions.spinner.styles.lualine').get_lualine_component(),
    },
  }
})

🎨 heirline

This style integrates with heirline.nvim to show spinner status in your statusline. It follows heirline's component-based architecture for maximum flexibility.

📋 Prerequisites: You must have rebelot/heirline.nvim installed and configured.

Demo: Note: I don't personally use heirline, so I haven't created a demo video for this spinner style. If you encounter any difficulties with the heirline integration or would like to contribute a demo video, please let me know - I'm happy to help troubleshoot and improve the implementation!

Visuals:

  • 🎨 Shows animated spinner with icons and text messages in your heirline status bar
  • 🔄 Updates through heirline's built-in event system
  • 📝 Displays messages like "🤖 Thinking...", "📨 Receiving...", "✅ Done!"
  • 🎯 Includes optional highlighting for better visibility

Specific Options: This spinner currently has no specific options.

opts = {
  style = "heirline",
}

-- In your heirline setup:
local heirline = require('heirline')
local CodeCompanionSpinner = require('codecompanion._extensions.spinner.styles.heirline')

heirline.setup({
  statusline = {
    -- Your other components...
    CodeCompanionSpinner.get_heirline_component(),
    -- More components...
  }
})

🪟 native

This style creates a highly configurable floating window using Neovim's native nvim_open_win function. It provides maximum control over the window's appearance and behavior without requiring any external dependencies.

📋 Prerequisites: None - uses only built-in Neovim features.

Demo:

native.mp4

Visuals:

  • 🏗️ A floating window appears with your configured title, size, position, and border style.
  • ⚙️ The window displays the current status with an animated spinner.
  • ✅ When complete, shows a "Done!" message before disappearing.

🔧 Specific Options:

opts = {
  style = "native",
  native = {
    -- How long (in ms) the "Done!" message should remain visible after completion.
    done_timer = 500,

    -- Window configuration - all nvim_open_win options are supported
    window = {
      -- Position and size
      relative = "editor",        -- "editor", "win", "cursor"
      width = 30,                 -- Window width in characters
      height = 1,                 -- Window height in lines
      row = vim.o.lines - 5,      -- Row position (from top)
      col = vim.o.columns - 35,   -- Column position (from left)

      -- Appearance
      style = "minimal",          -- Window style
      border = "rounded",         -- Border style: "none", "single", "double", "rounded", "solid", "shadow"
      title = "CodeCompanion",    -- Window title text
      title_pos = "center",       -- Title position: "left", "center", "right"

      -- Behavior
      focusable = false,          -- Whether window can receive focus
      noautocmd = true,           -- Disable autocmds for performance
    },

    -- Additional window options using nvim_set_option_value
    win_options = {
      -- winblend = 10,           -- Window transparency (0-100)
      -- winhighlight = "Normal:Normal,FloatBorder:Normal", -- Custom highlighting
    },
  },
}

🚀 Advanced Configuration Examples:

-- Top-right corner with transparency
native = {
  window = {
    relative = "editor",
    width = 25,
    height = 1,
    row = 1,
    col = vim.o.columns - 30,
    border = "single",
    title = "🤖 AI",
    title_pos = "left",
  },
  win_options = {
    winblend = 15,
  },
}

-- Centered with double border
native = {
  window = {
    relative = "editor",
    width = 35,
    height = 1,
    row = math.floor(vim.o.lines / 2) - 10,
    col = math.floor(vim.o.columns / 2) - 17,
    border = "double",
    title = "CodeCompanion Status",
    title_pos = "center",
  },
}

🚫 none

Disables all spinners and notifications from this extension.

opts = {
  style = "none",
}

🤝 Contributing

We welcome contributions! Please see our CONTRIBUTING.md file for detailed guidelines on how to contribute to this project. Feel free to open issues or discussions for this extension.

📋 When adding new spinner styles:

  1. 🔧 Follow the existing interface pattern (setup() and render() functions)
  2. 📦 Handle dependencies gracefully with warnings
  3. 🎯 Support all configuration states
  4. 📚 Add comprehensive documentation
  5. 🧪 Test with various CodeCompanion workflows

🌟 Project Sharing

This project was vibe coded using AI and made publicly available to help other Neovim users enhance their CodeCompanion experience with beautiful, customizable spinners. The code is freely available for anyone to use, modify, and contribute to.

🙏 Acknowledgements

This extension is built for and inspired by the excellent codecompanion.nvim plugin by Oliver Morris. While we drew inspirations from the spinners discussed in the CodeCompanion community and documented in their UI section, this implementation is our own original work with custom features and enhancements.

📚 Resources


💝 Vibe coded with love


About

Beautiful, configurable status spinners for codecompanion.nvim

Topics

Resources

License

Contributing

Stars

23 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors