Fast multi-format file previewer with inline Kitty graphics support, external pane previews, and integrations for file explorers and pickers.
glimpse.mp4
- πΌοΈ Inline rendering via Kitty Graphics Protocol (Kitty, Ghostty)
- π¬ Inline video playback via Kitty Animation Protocol (play/pause, seek); thumbnail fallback for other terminals
- π§ 3D model preview via f3d, with Blender fallback for
.blendfiles and configurable turntable animation - π Markdown preview via a configurable CLI renderer (leaf, glow, mdcat, pandoc) with full ANSI color support
- π Diagram preview - PlantUML (
.puml) and Mermaid (.mmd) rendered as inline images - π¦ Archive preview - list contents of zip/tar without extraction
- ποΈ SQLite preview - show tables and columns without modifying the database
- πΎ Binary preview - detect binaries with
fileand show a shortxxdhexdump - πͺ 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 (
<leader>pfor 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
- πΎ Auto-refresh on save - re-render open previews when the source file is saved
- π Contain resize (images always fully visible)
- Neovim >= 0.10
- ImageMagick (
magickCLI) - inline image conversion and font rendering - ffmpeg (optional) - video thumbnail extraction
- f3d (optional) - 3D model thumbnails and turntable frames
- Blender (optional) - fallback renderer for
.blendfiles - plantuml (optional) - PlantUML diagram rendering (requires Java)
- mmdc (optional) - Mermaid diagram rendering
- One of leaf, glow, mdcat, or pandoc (optional) - Markdown rendering
- OpenSSL (
opensslCLI) - certificate metadata extraction - file (
fileCLI) - binary type detection - xxd (
xxdCLI) - 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 | Dependency | Required |
|---|---|---|
| Inline image preview | magick |
Yes |
| Font rendering | magick |
Yes, with textual fallback if unavailable |
| Video preview | ffmpeg |
No |
| 3D model preview | f3d, with optional blender fallback for .blend |
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 |
| Markdown preview | leaf, glow, mdcat, pandoc, or cat |
First executable found wins |
| PlantUML preview | plantuml + Java runtime |
Yes when the PlantUML previewer is used |
| Mermaid preview | mmdc |
Yes when the Mermaid previewer is used |
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.
brew install imagemagick ffmpeg f3dsudo apt install imagemagick ffmpegsudo pacman -S imagemagick ffmpegmagick --version
f3d --version
file --version
xxd -h{
'adriancmiranda/glimpse.nvim',
ft = { 'oil', 'neo-tree' },
opts = {},
}The setup above loads Glimpse when entering a supported file explorer. If
auto_open = true, add event = 'BufReadPre' to the plugin spec so Glimpse can
register its automatic preview handler before the file finishes loading. This
intentionally applies to every file instead of duplicating the configurable
format list in the lazy-loading specification. keys.preview configures the
mapping installed by the file explorer integration; it is not a lazy.nvim
loading trigger.
The full default setup is available here as a reference when overriding only part of the configuration:
Default configuration
{
-- Rendering backend: 'auto', 'inline', or 'pane'. 'auto' uses inline
-- terminal graphics when supported, otherwise it falls back to a pane.
strategy = 'auto',
-- Render non-text previewable files as soon as they are opened.
auto_open = false,
-- Re-render open previews when the source file is saved.
auto_refresh = false,
-- External pane settings used when inline rendering is unavailable.
pane = {
position = 'right',
size = 40,
},
-- Inline image buffer behavior.
inline = {
rerender_on_tab = true,
close_with_q = true,
},
-- Default floating window sizes for text-like previews.
float = {
markdown = { width = 100 },
archive = { width = 70 },
cert = { width = 90 },
font = { width = 60 },
key = { width = 70 },
sqlite = { width = 80 },
binary = { width = 100 },
},
-- File explorer mappings installed by integrations such as Oil.nvim.
keys = {
preview = '<leader>p',
open = ';',
close = 'q',
},
-- Debounce intervals in milliseconds for background work.
debounce = {
prefetch = 200,
resize = 100,
},
-- Estimated terminal cell size in pixels, used for image scaling.
cell_size = {
width = 20,
height = 40,
},
-- Cache directory and expiration for generated preview artifacts.
cache = {
dir = vim.fn.stdpath('cache') .. '/glimpse',
max_age_days = 7,
},
-- Safety limits before invoking external tools on a file.
safety = {
max_file_size = 50 * 1024 * 1024, -- 50 MiB
},
-- Placeholder shown while an async preview is being generated.
loading = {
text = ' β³ Loading...',
},
-- Image formats rendered directly through the selected strategy.
image = {
formats = {
'.png', '.jpg', '.jpeg', '.gif', '.bmp',
'.webp', '.avif', '.svg', '.pdf', '.pict',
},
},
-- Video formats and inline animation settings.
video = {
formats = {
'.mp4', '.mkv', '.avi', '.mov',
'.webm', '.flv', '.wmv', '.m4v',
},
-- Optional external video opener: command string or function(filepath).
-- nil keeps videos in the inline preview flow.
open = nil,
frames = {
-- 'auto' | 'pipe' | 'poll' | 'batch'
-- 'pipe' streams frames via image2pipe (default, binary-safe, single temp file)
-- 'poll' writes numbered PNGs to a temp dir and polls for each
-- 'batch' extracts all frames first, then plays
strategy = 'auto',
per_second = 10,
limit = 120,
width = 640,
},
keys = {
toggle = '<CR>',
seek_forward = 'l',
seek_backward = 'h',
},
},
-- Markdown renderers are fallback candidates tried in order.
markdown = {
formats = { '.md', '.markdown', '.mdx', '.mdwn', '.mdown' },
tools = {
{ 'leaf', '--inline', 'ansi:{width}', '{input}' },
{ 'glow', '-s', 'dark', '--width', '{width}', '{input}' },
{ 'mdcat', '{input}' },
{ 'pandoc', '--to', 'plain', '--wrap', 'none', '{input}' },
{ 'cat', '{input}' },
},
},
-- Conversion pipelines map preview kinds or extensions to external commands.
pipelines = {
-- PlantUML reads from stdin and writes a PNG to the requested output file.
plantuml = {
-- steps run sequentially; each output becomes the next step's input.
steps = {
{
command = 'sh',
args = function(input, output)
return { '-c', 'plantuml -tpng -pipe < "$1" > "$2"', '--', input, output }
end,
},
},
},
-- Mermaid CLI writes the rendered diagram directly to the output path.
mermaid = {
-- args receives the source path and the output path Glimpse expects.
steps = {
{
command = 'mmdc',
args = function(input, output)
return { '-i', input, '-o', output }
end,
},
},
},
-- 3D models render to a static thumbnail by default.
model = {
-- A single static step produces one image for the preview.
steps = {
{
command = 'f3d',
output_ext = '.png',
args = function(input, output)
return { input, '--output', output, '--config=thumbnail' }
end,
},
},
-- renderer controls sequence playback when a pipeline produces frames.
renderer = {
fps = 12,
progressive = true,
},
-- keys apply to animated previews created from frame sequences.
keys = {
toggle = '<CR>',
seek_forward = 'l',
seek_backward = 'h',
},
},
-- Extension-specific pipelines override the generic model pipeline.
['.blend'] = {
-- previewers are fallbacks: Glimpse tries each command until one works.
previewers = {
-- Try f3d first for Blender files.
{
command = 'f3d',
output_ext = '.png',
args = function(input, output)
return { input, '--output', output, '--config=thumbnail' }
end,
},
-- Fall back to Blender itself when f3d is unavailable or fails.
{
command = 'blender',
output_ext = '.png',
args = function(input, output)
return {
'--background', input,
'--render-output', output:gsub('%.png$', '_'),
'--render-format', 'PNG',
'--render-frame', '1',
}
end,
output_pattern = function(output)
return output:gsub('%.png$', '_0001.png')
end,
},
},
},
},
-- Archive formats shown as read-only file listings.
archive = {
formats = {
'.zip', '.tar', '.tar.gz', '.tgz',
'.tar.bz2', '.tar.xz', '.txz',
'.jar', '.war', '.apk',
},
},
-- Optional integrations with file explorers and pickers.
integrations = {
oil = {
enable = true,
open = 'edit',
follow_cwd = true,
},
neotree = {
enable = false,
auto_preview = true,
},
telescope = {
enable = true,
-- Limit Glimpse previews to these Telescope pickers.
pickers = { 'find_files' },
follow_cwd = false,
-- Toggle individual preview kinds inside Telescope.
image = true,
video = true,
archive = true,
sqlite = true,
font = true,
cert = true,
key = true,
binary = true,
},
},
}Set auto_open = true to render non-text previewable files opened directly with
Neovim or selected normally in a file explorer. Native text formats such as
Markdown and PlantUML remain editable; use the preview key to render them.
Set auto_refresh = true to re-render open previews whenever the source file is
saved. Supports Markdown floats and pipeline-based previewers (PlantUML, Mermaid,
3D models). The refresh only runs while the preview window is still open; closing
the preview stops the hook automatically.
Use :GlimpsePreview to preview the current buffer without depending on a file
explorer integration:
:GlimpsePreview
:GlimpsePreview README.md
:GlimpsePreview %
:GlimpsePreview ~/models/scene.blendThe optional path supports file completion and expands relative paths, ~, and
%. Glimpse currently selects the preview destination automatically according
to the file type.
An optional [window] argument controls where the preview opens:
:GlimpsePreview right
:GlimpsePreview left README.md
:GlimpsePreview bottom ~/models/scene.blendValid values are float (default), right, left, bottom, and top.
:GlimpseClearCache removes cached conversions so the next preview re-runs from
scratch. Useful when a file has changed on disk but the cached version is stale,
or to free up disk space.
:GlimpseClearCache " clear everything
:GlimpseClearCache images " image conversions only (disk + memory)
:GlimpseClearCache previews " text preview cache only (markdown, sqlite, etc.)float.width and float.height override every floating preview. A preview-specific
entry such as float.markdown takes precedence over the global value. Numeric
widths use that many columns and numeric heights cap the content height.
width = 'auto' fills the available editor width; height = 'auto' fits the
content within the available editor height. Float margins are always respected.
Without overrides, the built-in sizes remain unchanged, except Markdown now
defaults to 100 columns.
The default pipeline renders a static thumbnail with f3d. To generate a progressive turntable animation instead, use a sequence step:
require('glimpse').setup({
pipelines = {
model = {
steps = {
{
command = 'f3d',
type = 'sequence',
frames = 36,
args = function(input, output, frame)
return {
input, '--output', output,
'--config=thumbnail',
'--camera-azimuth-angle=' .. (frame * 10),
}
end,
},
},
renderer = { fps = 12, progressive = true },
},
},
})To start the turntable paused and rotate manually, disable autoplay. The default
keys seek one frame backward/forward with h/l, and <CR> toggles playback:
renderer = { fps = 12, progressive = true, auto_play = false }
keys = { seek_backward = 'h', seek_forward = 'l', toggle = '<CR>' }.blend files try f3d first, then Blender when f3d is unavailable or fails.
Blender renders frame 1 with the camera and render settings stored in the
scene.
steps form a sequential conversion chain: each output becomes the next
step's input. previewers are alternatives: Glimpse tries them in order,
skipping missing commands and continuing after failed commands until one
produces a preview. An extension-specific entry such as pipelines['.blend']
takes precedence over the type-level entry such as pipelines.model.
require('glimpse').setup({
pipelines = {
model = {
previewers = {
{
command = 'first-model-renderer',
args = { '{input}', '--output', '{output}' },
},
{
command = 'fallback-model-renderer',
args = { '{input}', '--output', '{output}' },
},
},
},
},
})| Key | Action |
|---|---|
<leader>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.
Advanced: bind Oil float to current directory
-- Toggle open/close
vim.keymap.set('n', '<leader>e', function()
require('glimpse.integrations.oil').toggle_float()
end)
-- Always open (useful if you handle close separately via Oil's own keymap)
vim.keymap.set('n', '<leader>E', function()
require('glimpse.integrations.oil').open_float()
end)Both ensure the float opens at the directory Oil is currently browsing,
even when the cursor is on an image buffer. To close, use Oil's built-in
close keymap (default q inside the float).
Enable with integrations = { neotree = { enable = true } } in setup.
| Key | Action |
|---|---|
<leader>p |
Preview image/video side by side |
; |
Open image inline or video with external player |
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.
require('glimpse').setup({
integrations = {
telescope = {
enable = true,
},
},
})To choose the Telescope pickers that receive Glimpse previews:
require('glimpse').setup({
integrations = {
telescope = {
enable = true,
pickers = { 'find_files', 'git_files' },
},
},
})To disable specific preview kinds inside Telescope while keeping the integration enabled:
require('glimpse').setup({
integrations = {
telescope = {
enable = true,
pickers = { 'find_files', 'git_files' },
image = true,
video = true,
markdown = true,
model = true,
archive = true,
sqlite = false,
font = false,
cert = true,
key = true,
binary = false,
},
},
})If you prefer configuring Telescope manually, use the exported previewer:
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
- 3D models render through the configured model pipeline inside the Telescope preview pane
- 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.
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 tmuxglimpse.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.
- Symlinks are rejected (prevents reading unintended targets)
- Large files above
safety.max_file_sizeare 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)
| 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 |
| leaf / glow / mdcat / pandoc / cat | Markdown rendering | Markdown preview |
| plantuml | PlantUML diagram rendering | PlantUML preview |
| mmdc | Mermaid diagram rendering | Mermaid 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 version="1.0" encoding="UTF-8"?>
<!DOCTYPE policymap [
<!ELEMENT policymap (policy)*>
<!ATTLIST policymap xmlns CDATA #FIXED "">
<!ELEMENT policy EMPTY>
<!ATTLIST policy xmlns CDATA #FIXED "">
<!ATTLIST policy domain NMTOKEN #REQUIRED>
<!ATTLIST policy name NMTOKEN #IMPLIED>
<!ATTLIST policy pattern CDATA #IMPLIED>
<!ATTLIST policy rights NMTOKEN #IMPLIED>
<!ATTLIST policy stealth NMTOKEN #IMPLIED>
<!ATTLIST policy value CDATA #IMPLIED>
]>
<policymap>
<policy domain="resource" name="memory" value="256MiB"/>
<policy domain="resource" name="map" value="512MiB"/>
<policy domain="resource" name="disk" value="1GiB"/>
<policy domain="resource" name="time" value="30"/>
<policy domain="resource" name="thread" value="2"/>
<policy domain="resource" name="list-length" value="64"/>
<!--
"area" is an extra safety limit and may block very large images.
If Glimpse starts rejecting legitimate previews, remove or
increase it.
-->
<policy domain="resource" name="area" value="100MP"/>
</policymap>| 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 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:
set -ga update-environment WEZTERM_UNIX_SOCKETIf preview stops working, update manually:
# 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-<PID>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)
Markdown: MD, MARKDOWN, MDX, MDWN, MDOWN (requires leaf, glow, mdcat, pandoc, or cat)
Diagrams: PUML, PLANTUML, PU, WSD, IUML (PlantUML, requires plantuml + Java), MMD, MERMAID (Mermaid, requires mmdc)
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 -- Format detection (image, video, model, diagram, markdown...)
β βββ 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
β βββ pipeline.lua -- Steps-based conversion pipeline (run_steps, run_sequence)
β βββ pipeline_previewer.lua -- Shared runtime for pipeline-based previewers (tokens, animation, cleanup)
β βββ 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
β β βββ markdown.lua -- Markdown previewer (leaf/glow/mdcat/pandoc, terminal buffer)
β β βββ mermaid.lua -- Mermaid diagram previewer (mmdc)
β β βββ plantuml.lua -- PlantUML diagram previewer (plantuml -pipe)
β β βββ 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)- snacks.nvim - inspiration for the rendering protocol
- Yazi - inspiration for performance optimizations
- Reddit post - original WezTerm preview concept