https://github.com/adriancmiranda/glimpse.nvim
🖼️ Fast multi-format file previewer with inline Kitty graphics support, external pane previews, and integrations for file explorers and pickers.
https://github.com/adriancmiranda/glimpse.nvim
image-preview iterm2 kitty-graphics kitty-graphics-protocol lua neo-tree neovim neovim-plugin oil-nvim sixel wezterm
Last synced: 3 days ago
JSON representation
🖼️ Fast multi-format file previewer with inline Kitty graphics support, external pane previews, and integrations for file explorers and pickers.
- Host: GitHub
- URL: https://github.com/adriancmiranda/glimpse.nvim
- Owner: adriancmiranda
- License: mit
- Created: 2026-05-17T04:47:58.000Z (26 days ago)
- Default Branch: main
- Last Pushed: 2026-06-07T20:39:11.000Z (5 days ago)
- Last Synced: 2026-06-07T21:23:09.713Z (5 days ago)
- Topics: image-preview, iterm2, kitty-graphics, kitty-graphics-protocol, lua, neo-tree, neovim, neovim-plugin, oil-nvim, sixel, wezterm
- Language: Lua
- Homepage:
- Size: 261 KB
- Stars: 3
- Watchers: 0
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
- awesome-neovim - adriancmiranda/glimpse.nvim - Fast multi-format file previewer with inline kitty graphics support, external pane previews, and integrations for file explorers and pickers. (File Explorer / Assembly)
README
# glimpse.nvim
> Fast multi-format file previewer with inline Kitty graphics support,
> external pane previews, and integrations for file explorers and pickers.
## Features
- 🖼️ Inline rendering via **Kitty Graphics Protocol** (Kitty, Ghostty)
- 🎬 **Video preview** via ffmpeg thumbnail extraction (cached)
- 📦 **Archive preview** - list contents of zip/tar without extraction
- 🗄️ **SQLite preview** - show tables and columns without modifying the database
- 💾 **Binary preview** - detect binaries with `file` and show a short `xxd` hexdump
- 🪟 External pane via **WezTerm CLI**, **kitten icat**, **iTerm imgcat**
- 🎨 **Font rendering** via ImageMagick, with a textual fallback when rendering is unavailable
- 🎨 **Sixel** fallback for terminals without Kitty Graphics support
- 📂 **Oil.nvim** integration (`p` for preview, `;` to open)
- 🔭 **Telescope** integration (scoped previews for images, videos, archives, SQLite, fonts, keys, certificates, and binaries)
- 🌳 **Neo-tree** integration
- 🔐 **Certificate preview** - show subject, issuer, validity, and
fingerprint for `.crt`/`.pem`
- ⚡ Image conversion cache + background prefetch
- 🔄 Auto re-render on window resize or tab switch
- 📐 Contain resize (images always fully visible)
## Requirements
- Neovim >= 0.10
- [ImageMagick](https://imagemagick.org/) (`magick` CLI) - inline image conversion and font rendering
- [ffmpeg](https://ffmpeg.org/) (optional) - video thumbnail extraction
- [OpenSSL](https://www.openssl.org/) (`openssl` CLI) - certificate metadata extraction
- [file](https://man7.org/linux/man-pages/man1/file.1.html) (`file` CLI) - binary type detection
- [xxd](https://vimhelp.org/xxd.txt.html) (`xxd` CLI) - binary hexdump rendering
- Terminal with support for at least one protocol:
- **Kitty Graphics** (recommended): Kitty, Ghostty
- **Terminal CLI**: WezTerm, iTerm2
- **Sixel**: xterm, foot, mlterm, contour
> [!NOTE]
> `magick` is required for inline image rendering and font rendering. Font preview still has a textual fallback if rendering is unavailable.
### Feature Dependencies
| Feature | Dependency | Required |
|---|---|---|
| Inline image preview | `magick` | Yes |
| Font rendering | `magick` | Yes, with textual fallback if unavailable |
| Video preview | `ffmpeg` | No |
| Archive preview | `zipinfo`, `tar` | Yes when the archive type is used |
| SQLite preview | `sqlite3` | Yes when the SQLite preview is used |
| Certificate preview | `openssl` | Yes when the certificate preview is used |
| Binary preview | `file`, `xxd` | Yes when the binary preview is used |
| SSH/GPG key preview | `ssh-keygen`, `gpg` | Yes when the key preview is used |
## Installing dependencies
> [!NOTE]
> Binary preview depends on `file` and `xxd`. They are usually available on Unix-like systems, but if your OS does not ship them by default, install them separately.
### macOS (Homebrew)
```bash
brew install imagemagick ffmpeg
```
### Linux (apt)
```bash
sudo apt install imagemagick ffmpeg
```
### Linux (pacman)
```bash
sudo pacman -S imagemagick ffmpeg
```
### Verify installation
```bash
magick --version
file --version
xxd -h
```
## Usage
### Setup (lazy.nvim)
```lua
{
'adriancmiranda/glimpse.nvim',
ft = { 'oil', 'neo-tree' },
event = {
'BufReadPre *.png', 'BufReadPre *.jpg', 'BufReadPre *.jpeg',
'BufReadPre *.gif', 'BufReadPre *.bmp', 'BufReadPre *.webp',
'BufReadPre *.avif', 'BufReadPre *.svg', 'BufReadPre *.pdf',
'BufReadPre *.ttf', 'BufReadPre *.otf',
'BufReadPre *.crt', 'BufReadPre *.pem',
'BufReadPre *.zip', 'BufReadPre *.tar', 'BufReadPre *.tgz',
'BufReadPre *.jar', 'BufReadPre *.war', 'BufReadPre *.apk',
'BufReadPre *.db', 'BufReadPre *.sqlite', 'BufReadPre *.sqlite3',
},
opts = {
strategy = 'auto', -- 'auto' | 'inline' | 'pane'
pane = {
position = 'right', -- 'right' | 'bottom'
size = 40, -- split/pane size percentage
},
inline = {
rerender_on_tab = true, -- re-render when switching back to image tab
close_with_q = true, -- map key to close image buffer
},
keys = {
preview = 'p', -- preview image/video side by side (Oil)
open = ';', -- open image (configurable: current tab or new tab) (Oil)
close = 'q', -- close image buffer
},
debounce = {
prefetch = 200, -- ms before pre-converting on cursor move
resize = 100, -- ms before re-rendering on resize
},
cell_size = {
width = 20, -- estimated pixels per terminal column
height = 40, -- estimated pixels per terminal row
},
cache = {
dir = vim.fn.stdpath('cache') .. '/glimpse',
max_age_days = 7, -- auto-remove cached files older than N days (0 to disable)
},
safety = {
max_file_size = 50 * 1024 * 1024, -- skip files larger than 50MB
},
loading = {
text = ' ⏳ Loading...',
},
image = {
formats = { -- supported image extensions
'.png', '.jpg', '.jpeg', '.gif', '.bmp',
'.webp', '.avif', '.svg', '.pdf', '.pict',
},
},
video = {
formats = { -- supported video extensions (requires ffmpeg)
'.mp4', '.mkv', '.avi', '.mov',
'.webm', '.flv', '.wmv', '.m4v',
},
open = nil, -- command or function to open videos externally
-- string: 'open' (macOS), 'xdg-open' (Linux)
-- function: fun(filepath) for custom logic
-- nil: opens as buffer in Neovim
},
archive = {
formats = { -- supported archive extensions (preview only, no extraction)
'.zip', '.tar', '.tar.gz', '.tgz',
'.tar.bz2', '.tar.xz', '.txz',
'.jar', '.war', '.apk',
},
},
integrations = {
oil = {
enable = true, -- keymaps in Oil
open = 'edit', -- 'edit' | 'tabedit' | function(filepath)
},
neotree = { -- Neo-tree integration
enable = false, -- enable auto-preview in Neo-tree
auto_preview = true, -- preview on cursor move (set false to disable)
},
telescope = {
enable = true, -- enables image/video previews in :Telescope find_files
},
},
},
}
```
Breaking change: the public config was reorganized into nested tables.
### Keymaps (Oil.nvim)
| Key | Action |
|-----|--------|
| `p` | Preview image/video side by side (reuses window) |
| `;` | Open image (configurable: current tab or new tab) |
| `q` | Close image buffer and residual empty window |
When an image is opened, the current window follows that file's directory, so `Oil.nvim` opens in the same folder.
### Keymaps (Neo-tree)
Enable with `integrations = { neotree = { enable = true } }` in setup.
| Key | Action |
|-------|------|
| `p` | Preview image/video side by side |
| `;` | Open image inline or video with external player |
### Telescope
Enable with `integrations = { telescope = { enable = true } }` in setup. With lazy.nvim,
glimpse applies its previewer when telescope.nvim loads. By default, only
`:Telescope find_files` receives Glimpse previews.
```lua
require('glimpse').setup({
integrations = {
telescope = {
enable = true,
},
},
})
```
To choose the Telescope pickers that receive Glimpse previews:
```lua
require('glimpse').setup({
integrations = {
telescope = {
enable = true,
pickers = { 'find_files', 'git_files' },
},
},
})
```
To disable specific preview kinds inside Telescope while keeping the integration
enabled:
```lua
require('glimpse').setup({
integrations = {
telescope = {
enable = true,
pickers = { 'find_files', 'git_files' },
image = true,
video = true,
archive = true,
sqlite = false,
font = false,
cert = true,
key = true,
binary = false,
},
},
})
```
If you prefer configuring Telescope manually, use the exported previewer:
```lua
require('telescope').setup({
pickers = {
find_files = {
previewer = require('glimpse.integrations.telescope').previewer(),
},
},
})
```
- **Images** are rendered inline via Kitty Graphics Protocol with a 100ms debounce
- **Videos** extract a thumbnail via ffmpeg before rendering
- **Archives, SQLite databases, fonts, keys, certificates, and binaries** use the matching Glimpse previewer inside the Telescope preview pane
- **Other files** fall back to Telescope's default previewer
Switching between files is fast after thumbnails and conversions are cached.
### API
```lua
local img = require('glimpse')
img.can_preview(filepath) -- check whether Glimpse knows how to preview it
img.get_preview_kind(filepath) -- return the preview kind that would be used
img.show(filepath) -- show image or video thumbnail
img.preview(filepath) -- show reusing existing window
img.close() -- close active preview
img.is_image(filepath) -- check if supported image
img.is_video(filepath) -- check if supported video
img.is_archive(filepath) -- check if supported archive
img.is_sqlite(filepath) -- check if supported SQLite database
img.is_previewable(filepath) -- check if supported previewable file
img.is_cert(filepath) -- check if supported certificate
img.is_font(filepath) -- check if supported font
img.is_key(filepath) -- check if supported GPG/SSH key
img.get_terminal() -- return detected terminal
img.supports_inline() -- check whether inline rendering is supported
img.in_tmux() -- check whether Neovim runs inside tmux
```
## Security & Privacy
glimpse.nvim runs **only local commands** on files you explicitly select.
It never makes network requests or sends data externally.
> [!IMPORTANT]
> The plugin only uses local tools on files you explicitly select. When a tool is missing, the affected previewer fails safely instead of breaking the rest of the plugin.
### File validation
- **Symlinks** are rejected (prevents reading unintended targets)
- **Large files** above `safety.max_file_size` are skipped (default: 50MB)
- **SVG files** are processed with restricted XML parsing (no entity expansion, no external resources)
- **Archive preview** never extracts files, only reads metadata
- **Shell commands** use list arguments (no shell interpolation)
### External tools used
| Tool | Purpose | When |
|------|---------|------|
| magick (ImageMagick) | Image resize/conversion | Image preview |
| ffmpeg | Video thumbnail extraction | Video preview |
| openssl | X.509 certificate metadata extraction | Certificate preview |
| zipinfo | Archive listing (read-only) | Archive preview |
| tar | Archive listing (read-only) | tar/tgz preview |
| sqlite3 | Schema listing (read-only) | SQLite preview |
No files are extracted, modified, or uploaded. All processing is local and read-only.
Optional: ImageMagick resource policy
Optional extra protection: place this in `~/.config/ImageMagick/policy.xml`.
```xml
]>
```
## Supported terminals
| Terminal | Strategy | Method |
|----------|----------|--------|
| Kitty | inline | Kitty Graphics + unicode placeholders |
| Ghostty | inline | Kitty Graphics + unicode placeholders |
| WezTerm | pane | `wezterm cli split-pane` + `wezterm imgcat` |
| iTerm2 | pane | `imgcat` |
| xterm/foot/mlterm | pane (sixel) | `magick ... sixel:-` via tmux |
### WezTerm + tmux
`wezterm cli` needs access to the WezTerm GUI socket. Inside tmux, the
`WEZTERM_UNIX_SOCKET` variable can become stale if WezTerm restarts.
Add to `tmux.conf`:
```bash
set -ga update-environment WEZTERM_UNIX_SOCKET
```
If preview stops working, update manually:
```bash
# Find the active socket
ls ~/.local/share/wezterm/gui-sock-*
# Export the correct one
tmux set-environment WEZTERM_UNIX_SOCKET ~/.local/share/wezterm/gui-sock-
```
## Supported formats
**Images:** PNG, JPG, JPEG, GIF, BMP, WebP, AVIF, SVG, PDF, PICT
**Certificates:** CRT, PEM (X.509 certificates)
**Videos:** MP4, MKV, AVI, MOV, WebM, FLV, WMV, M4V (requires ffmpeg)
## Architecture
```bash
lua/
├── glimpse/
│ ├── init.lua -- Public API: setup(), show(), preview(), close(), helpers
│ ├── detect.lua -- Terminal detection via tmux client_termname
│ ├── kitty.lua -- Kitty Graphics Protocol (transmit, delete, prefetch)
│ ├── renderer.lua -- Placement management and extmarks
│ ├── sixel.lua -- Sixel protocol (fallback)
│ ├── thumbnail.lua -- Video thumbnail extraction (ffmpeg, async)
│ ├── magickwand.lua -- FFI bindings for libMagickWand
│ ├── util.lua -- Image, video and certificate format detection
│ ├── archive.lua -- Archive listing and suspicious path detection
│ ├── font.lua -- Font metadata extraction and rendering
│ ├── sqlite.lua -- SQLite schema preview
│ ├── safety.lua -- File validation and safety checks
│ ├── previewer/
│ │ ├── archive.lua -- Archive previewer
│ │ ├── cert.lua -- X.509 certificate previewer
│ │ ├── binary.lua -- Binary previewer (file + hexdump)
│ │ ├── font.lua -- Font previewer
│ │ ├── image.lua -- Inline image previewer
│ │ ├── key.lua -- GPG/SSH key previewer
│ │ └── sqlite.lua -- SQLite previewer
│ ├── strategy/
│ │ ├── inline.lua -- Inline rendering + autocmds
│ │ └── pane.lua -- External pane rendering (WezTerm, iTerm2)
│ └── integrations/
│ ├── oil.lua -- Oil.nvim integration
│ ├── neotree.lua -- Neo-tree integration (auto-preview)
│ └── telescope.lua -- Telescope integration (scoped picker preview)
```
## Credits
- [snacks.nvim](https://github.com/folke/snacks.nvim) - inspiration for the rendering protocol
- [Yazi](https://github.com/sxyazi/yazi) - inspiration for performance optimizations
- [Reddit post](https://www.reddit.com/r/neovim/comments/1e1txpz/) - original WezTerm preview concept