{"id":25994759,"url":"https://github.com/iamgideonidoko/loft.nvim","last_synced_at":"2026-03-01T07:09:31.070Z","repository":{"id":273856413,"uuid":"917676134","full_name":"iamgideonidoko/loft.nvim","owner":"iamgideonidoko","description":"Streamlined plugin for productive buffer management.","archived":false,"fork":false,"pushed_at":"2026-02-25T11:43:11.000Z","size":288,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-25T11:44:48.053Z","etag":null,"topics":["buffer","buffer-management","lua","neovim","neovim-plugin","nvim","nvim-plugin"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iamgideonidoko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-01-16T12:48:58.000Z","updated_at":"2026-02-25T11:43:15.000Z","dependencies_parsed_at":"2025-02-07T13:33:59.349Z","dependency_job_id":"76243c2e-14d5-46dc-98f9-08ba08d1775a","html_url":"https://github.com/iamgideonidoko/loft.nvim","commit_stats":null,"previous_names":["iamgideonidoko/loft.nvim"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/iamgideonidoko/loft.nvim","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgideonidoko%2Floft.nvim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgideonidoko%2Floft.nvim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgideonidoko%2Floft.nvim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgideonidoko%2Floft.nvim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iamgideonidoko","download_url":"https://codeload.github.com/iamgideonidoko/loft.nvim/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamgideonidoko%2Floft.nvim/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29963120,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T06:55:38.174Z","status":"ssl_error","status_checked_at":"2026-03-01T06:53:04.810Z","response_time":124,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["buffer","buffer-management","lua","neovim","neovim-plugin","nvim","nvim-plugin"],"created_at":"2025-03-05T15:16:49.176Z","updated_at":"2026-03-01T07:09:31.054Z","avatar_url":"https://github.com/iamgideonidoko.png","language":"Lua","readme":"\u003cdiv align=\"center\"\u003e\n  \u003ch3 align=\"center\"\u003e\u003ccode\u003e ⨳⨳ LOFT ⨳⨳ \u003c/code\u003e\u003c/h3\u003e\n  \u003cp align=\"center\"\u003eThe missing buffer management tool\u003cbr /\u003eA sleek, no-nonsense plugin built to tame your buffers and supercharge your flow.\u003c/p\u003e\n\u003c/div\u003e\n\u003cbr /\u003e\n\n## Table of Content\n\n- [Introduction](#introduction)\n- [Installation](#installation)\n- [Configuration](#configuration)\n  - [Default Options](#default-options)\n  - [Persistence options](#persistence-options-persistence)\n  - [Session plugin compatibility](#session-plugin-compatibility)\n- [Commands](#commands)\n- [Highlights](#highlights)\n- [Window options](#window-options)\n- [Autocmds](#autocmds)\n- [Public API](#public-api)\n- [Roadmap](#roadmap)\n\n## Introduction\n\nLoft is a powerful yet lightweight Neovim plugin that makes buffer management fast, intuitive, and frustration-free—so you can focus on what truly matters.\n\n### 🛑 The Problem: Buffer Chaos!\n\nImagine your Neovim buffer list is like a messy desk. You start with a clean workspace, but as the day goes on, files pile up—some important, others just distractions. Before you know it, you’re **digging through a jungle of buffers**, closing the wrong ones, and losing track of key files.\n\nEver rage-quit Neovim just to start fresh? You’re not alone.\n\n### ✅ The Solution: Loft\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"assets/showcase.png\" alt=\"Showcase\" /\u003e\n\u003c/div\u003e\n\nLoft uses a registry to manage state and track buffers that can be cyclically navigated to. It provides a floating UI that lists these buffers as rearrangeable entries in order of recency from bottom to top. The catch is in Loft's flagship features—**Smart Ordering** and **Marking**:\n\n- #### ⟅⇅⟆ Smart Ordering\n\n  The smart ordering feature (represented with the symbol `⟅⇅⟆`) dynamically arranges your buffers based on recency. This means that if you navigate to a buffer without any Loft's action (say via [Telescope](https://github.com/nvim-telescope/telescope.nvim)), that buffer will be move to the last position in the registry. Also, the current buffer before navigation will be moved to second to the last position.\n\n- #### (✓) Marking\n\n  The marking feature allows you to bookmark important buffers. The marked buffers/entries (identified by the symbol `(✓)`) can be specially navigated to cyclically or by keybinding. You read right, keybinding; the nine most recent buffers are automatically mapped for quick access anytime.\n\nNo more scrambling to find where you left off. No more accidental closures. **Just smooth, intelligent buffer management.** 🔥\n\n## Installation\n\n**Note**: Loft requires Neovim 0.8+\n\nUsing [lazy.nvim](https://github.com/folke/lazy.nvim):\n\n```lua\n{\n  \"iamgideonidoko/loft.nvim\",\n  config = true, -- Calls setup automatically\n}\n```\n\n## Configuration\n\nYou need to call the plugin's `setup()` method if you haven't yet:\n\n```lua\nrequire(\"loft\").setup()\n```\n\n### Default Options\n\n```lua\nlocal actions = require(\"loft.actions\")\nrequire(\"loft\").setup({\n  close_invalid_buf_on_switch = true, -- Whether to close invalid buffers during navigation\n  enable_smart_order_by_default = true, -- Whether to enable smart order by default\n  smart_order_marked_bufs = false, -- Whether smart order (`⟅⇅⟆`) should reposition marked buffers\n  smart_order_alt_bufs = true, -- Whether smart order (`⟅⇅⟆`) should reposition alternate buffers\n  -- When false (default), switching focus to a different window split or tab will NOT\n  -- reorder the registry. Smart reordering only applies when the buffer itself changes\n  -- within the same window. Set to true to restore the old behaviour.\n  smart_order_on_window_switch = false,\n  enable_recent_marked_mapping = true, -- Whether the 9 most recently marked buffers should be switched to with a mapping (with keymaps)\n\n  -- The character to use after leader when assigning keymap to the 9 most recently marked buffers\n  post_leader_marked_mapping = \"l\",  -- Maps to \u003cleader\u003el1...9 for navigation\n  show_marked_mapping_num = true, -- Whether to show the mapping number for the 9 most recently marked buffers\n  marked_mapping_num_style = \"solid\", -- The style of the mapping number\n\n  --[[ The timeout in milliseconds to wait before closing the UI after moving the current buffer.\n  Defaults to 800. Set to 0 to disable the UI from showing at all. ]]\n  ui_timeout_on_curr_buf_move = 800,\n\n  -- Display the buffer list in reverse order (first/oldest registry entry at the bottom).\n  -- All navigation, reordering, and marked-buffer jumps continue to work correctly.\n  reverse_order = false,\n\n  -- Whether to show a confirmation prompt before force-deleting buffers (D / visual D).\n  -- Set to false to skip the prompt (useful for advanced users or CI environments).\n  confirm_force_delete = true,\n\n  -- Whether deleting the current buffer (marked ●) from the Loft UI is allowed.\n  -- Set to false to prevent the current buffer from being deleted via the UI, which can be a helpful safeguard against accidental closures.\n  allow_delete_current_buffer = true,\n\n  -- Whether to automatically force-delete (from Neovim) any buffer whose backing\n  -- file no longer exists on disk. When true (default) Loft removes such buffers\n  -- during every clean() call, preventing navigation to missing files. Set to false\n  -- to keep those buffers open — useful if you manage deletion yourself or work\n  -- with remote/virtual file systems where files may temporarily disappear.\n  auto_delete_missing_file_bufs = true,\n\n  -- List of `buftype` values whose buffers are never tracked by Loft.\n  -- e.g. { \"terminal\", \"quickfix\", \"nofile\" }\n  -- Defaults to {} (track all buftypes).\n  exclude_buftypes = {},\n\n  --[[ Where to position the cursor when the Loft UI opens:\n    \"cursor\"  — restore the last cursor line from the previous close (falls back to \"current\" on first open)\n    \"top\"     — always start at the first entry in the list\n    \"current\" — always jump to the ● active-buffer entry (default)\n    \"middle\"  — always start at the middle entry\n    \"bottom\"  — always start at the last entry ]]\n  open_at = \"current\",\n\n  -- ── Main UI window ───────────────────────────────────────────────────────\n  window = {\n    width = nil,   -- Explicit width; defaults to 80% of editor columns\n    height = nil,  -- Explicit height; defaults to registry size capped at 80% of lines\n\n    -- Positioning: row/col override the default centred calculation.\n    -- row_offset/col_offset are added on top of whichever row/col is used.\n    row = nil,        -- Explicit row (0-indexed from top of editor)\n    col = nil,        -- Explicit col (0-indexed from left of editor)\n    row_offset = 0,   -- Shift window vertically (positive = down)\n    col_offset = 0,   -- Shift window horizontally (positive = right)\n\n    -- Title and footer (Neovim 0.9+ for title, 0.10+ for footer).\n    -- When nil the auto-generated Loft title / smart-order indicator is used.\n    title = nil,       -- Custom title string (nil = auto)\n    title_pos = \"center\",\n    footer = nil,      -- Custom footer string (nil = smart-order indicator)\n    footer_pos = \"center\",\n\n    zindex = 100,\n    border = \"rounded\",  -- \"none\"|\"single\"|\"double\"|\"rounded\"|\"solid\"|\"shadow\"|string[]\n  },\n\n  -- ── Help window ──────────────────────────────────────────────────────────\n  -- Opened with the `show_help` keymap (default: `?`).\n  -- Inherits border from the main window when not set.\n  -- zindex is always clamped to \u003e main window zindex.\n  help_window = {\n    disable = false,  -- Set to true to disable the help window entirely\n\n    width = nil,   -- Defaults to 70\n    height = nil,  -- Defaults to content height capped at 80% of lines\n\n    row = nil,        -- Explicit row (nil = centred)\n    col = nil,        -- Explicit col (nil = centred)\n    row_offset = 0,\n    col_offset = 0,\n\n    border = nil,     -- Inherits main window border when nil\n    zindex = nil,     -- Defaults to main zindex + 10; always clamped \u003e main zindex\n  },\n  keymaps = {\n    --NB: all movements/navigations are cyclic\n    -- Keybindings specific to Loft main UI (normal mode)\n    ui = {\n      [\"k\"] = \"move_up\", -- Move cursor up\n      [\"j\"] = \"move_down\", -- Move cursor down\n      [\"\u003cC-k\u003e\"] = \"move_entry_up\", -- Move entry (+buffer)\n      [\"\u003cC-j\u003e\"] = \"move_entry_down\", -- Move entry (+buffer)\n      [\"dd\"] = \"delete_entry\", -- Delete entry (+buffer)\n      [\"D\"] = \"force_delete_entry\", -- Force delete entry (no save prompt)\n      [\"\u003cCR\u003e\"] = \"select_entry\", -- Select entry (+buffer)\n      [\"\u003cEsc\u003e\"] = \"close\", -- Close Loft\n      [\"q\"] = \"close\",\n      [\"m\"] = \"toggle_mark_entry\", -- Mark or unmark entry\n      [\"\u003cC-s\u003e\"] = \"toggle_smart_order\", -- Enable or disable smart order status\n      [\"?\"] = \"show_help\", -- Show Loft help menu\n      [\"\u003cM-k\u003e\"] = \"move_up_to_marked_entry\", -- Move up to the next marked entry\n      [\"\u003cM-j\u003e\"] = \"move_down_to_marked_entry\", -- Move down to the next marked entry\n    },\n    -- Keybindings specific to Loft main UI (visual mode)\n    ui_visual = {\n      [\"d\"] = \"delete_selected_entries\", -- Delete visually selected entries\n      [\"D\"] = \"force_delete_selected_entries\", -- Force delete selected entries (no save)\n    },\n    -- Keybindings specific to editor\n    general = {\n      [\"\u003cleader\u003elf\"] = actions.open_loft, -- Open Loft\n      [\"\u003cTab\u003e\"] = actions.switch_to_next_buffer, -- Navigate to the next buffer\n      [\"\u003cS-Tab\u003e\"] = actions.switch_to_prev_buffer, -- Navigate to the prev buffer\n      [\"\u003cleader\u003ex\"] = actions.close_buffer, -- Close buffer\n      [\"\u003cleader\u003eX\"] = {\n        callback = function()\n          actions.close_buffer({ force = true })\n        end,\n        desc = \"Force close buffer\",\n      },\n      [\"\u003cleader\u003eln\"] = actions.switch_to_next_marked_buffer, -- Navigate to the next marked buffer\n      [\"\u003cleader\u003elp\"] = actions.switch_to_prev_marked_buffer, -- Navigate to the previous marked buffer\n      [\"\u003cleader\u003elm\"] = actions.toggle_mark_current_buffer, -- Mark or unmark the current buffer\n      [\"\u003cleader\u003els\"] = actions.toggle_smart_order, -- Toggle Smart Order ON and OFF\n      [\"\u003cleader\u003ela\"] = actions.switch_to_alt_buffer, -- Switch to alternate buffer without updating the registry\n      [\"\u003cS-M-i\u003e\"] = actions.move_buffer_up, --  Move the current buffer up while showing the UI briefly\n      [\"\u003cS-M-o\u003e\"] = actions.move_buffer_down, --  Move the current buffer down while showing the UI briefly\n    },\n  },\n  -- Session persistence: saves registry order, marks and smart order state to disk\n  -- per working directory, and restores them on the next startup.\n  persistence = {\n    enabled = false, -- Opt-in: set to true to enable\n    path = nil,      -- Defaults to stdpath(\"data\")/loft/\u003ccwd_hash\u003e.json\n  },\n})\n```\n\n### Session plugin compatibility\n\nWhen `persistence.enabled = true`, loft hooks into the following events to\nrestore state after a session is loaded:\n\n| Plugin                                                                            | Event hooked                                   |\n| --------------------------------------------------------------------------------- | ---------------------------------------------- |\n| Native `:mksession` / any `:source session.vim`                                   | `SessionLoadPost` (Neovim built-in)            |\n| [folke/persistence.nvim](https://github.com/folke/persistence.nvim)               | `User PersistenceLoadPost` + `SessionLoadPost` |\n| [olimorris/persisted.nvim](https://github.com/olimorris/persisted.nvim)           | `User PersistedLoadPost` + `SessionLoadPost`   |\n| [stevearc/resession.nvim](https://github.com/stevearc/resession.nvim)             | `User ResessionLoadPost`                       |\n| [rmagatti/auto-session](https://github.com/rmagatti/auto-session)                 | `SessionLoadPost`                              |\n| [Shatur/neovim-session-manager](https://github.com/Shatur/neovim-session-manager) | `SessionLoadPost`                              |\n| No session plugin                                                                 | deferred `VimEnter` fallback                   |\n\n**possession.nvim note**: [jedrzejboczar/possession.nvim](https://github.com/jedrzejboczar/possession.nvim) executes sessions via `nvim_exec2` instead of `:source`, so neither `SessionLoadPost` nor a post-load User event fires. Call loft's restore manually in your `after_load` hook:\n\n```lua\nrequire(\"possession\").setup({\n  hooks = {\n    after_load = function()\n      local p = require(\"loft.persistence\")\n      local cfg = require(\"loft.config\").all.persistence\n      p.restore(require(\"loft.registry\"), cfg)\n    end,\n  },\n})\n```\n\n### Persistence options (`persistence`)\n\n| Option    | Type          | Default                                | Description                                            |\n| --------- | ------------- | -------------------------------------- | ------------------------------------------------------ |\n| `enabled` | `boolean`     | `false`                                | Opt-in: set to `true` to persist state across sessions |\n| `path`    | `string\\|nil` | `stdpath(\"data\")/loft/\u003ccwd_hash\u003e.json` | Custom file path for the saved state                   |\n\nWhen `enabled = true`, Loft saves the registry order, buffer marks, and smart-order\nstate to a JSON file on disk keyed by the current working directory. State is restored\nautomatically the next time Neovim opens in the same directory.\n\nSee [Session plugin compatibility](#session-plugin-compatibility) above for per-plugin\nrestore hooks.\n\n## Commands\n\n| Command                 | Description                                                                              |\n| ----------------------- | ---------------------------------------------------------------------------------------- |\n| `:LoftToggle`           | Open or close the Loft UI.                                                               |\n| `:LoftToggleSmartOrder` | Enable or disable the smart order feature.                                               |\n| `:LoftToggleMark`       | Toggle mark on the current buffer.                                                       |\n| `:LoftCloseOthers`      | Close all buffers except the current one.                                                |\n| `:LoftCloseOthers!`     | Force-close all other buffers (ignores modified state).                                  |\n| `:LoftCloseUnmarked`    | Close all unmarked buffers. Mark what you want to keep, then run this to clear the rest. |\n| `:LoftCloseUnmarked!`   | Force-close all unmarked buffers.                                                        |\n\n## Native UI keymaps\n\nLoft uses **normal-mode** mappings so you never have to enter insert mode.\n\n### Normal mode (inside Loft)\n\n| Key         | Action                       | Notes                                                             |\n| ----------- | ---------------------------- | ----------------------------------------------------------------- |\n| `k`         | Move cursor up (cyclic)      |                                                                   |\n| `j`         | Move cursor down (cyclic)    |                                                                   |\n| `\u003cC-k\u003e`     | Move entry up in list        |                                                                   |\n| `\u003cC-j\u003e`     | Move entry down in list      |                                                                   |\n| `dd`        | Delete entry + buffer        | Closes the buffer (no save prompt)                                |\n| `D`         | Force-delete entry + buffer  | Bypasses modified check; prompts if `confirm_force_delete = true` |\n| `m`         | Toggle mark on entry         |                                                                   |\n| `\u003cCR\u003e`      | Select entry (switch to buf) |                                                                   |\n| `q`/`\u003cEsc\u003e` | Close Loft                   |                                                                   |\n| `\u003cC-s\u003e`     | Toggle smart order           |                                                                   |\n| `?`         | Open help window             |                                                                   |\n| `\u003cM-k\u003e`     | Jump to prev marked entry    |                                                                   |\n| `\u003cM-j\u003e`     | Jump to next marked entry    |                                                                   |\n\n### Visual mode (inside Loft)\n\nSelect multiple lines with `V` then:\n\n| Key | Action                            | Notes                                         |\n| --- | --------------------------------- | --------------------------------------------- |\n| `d` | Delete selected entries + buffers |                                               |\n| `D` | Force-delete selected entries     | Prompts once if `confirm_force_delete = true` |\n\n### Disabling or remapping\n\nSet any keymap to `false` to disable it, or provide a different key:\n\n```lua\nrequire(\"loft\").setup({\n  keymaps = {\n    ui = {\n      [\"D\"] = false,       -- Disable force-delete\n      [\"\u003cC-d\u003e\"] = \"delete_entry\", -- Add back an old-style binding\n    },\n    ui_visual = {\n      [\"D\"] = false,       -- Disable visual force-delete\n    },\n  },\n  confirm_force_delete = false, -- Skip confirmation prompt\n})\n```\n\n## Highlights\n\nLoft applies dedicated highlight groups to its UI so the buffer list is visually\nscannable at a glance and fully themeable. All groups use `default = true`, meaning\nyour colorscheme (or your own `vim.api.nvim_set_hl` calls) take priority — you never\nneed to clear Loft's definitions first.\n\n### Highlight groups\n\n| Group                  | Default link     | Applied to                                              |\n| ---------------------- | ---------------- | ------------------------------------------------------- |\n| `LoftCurrentBuffer`    | `PmenuSel`       | Full line — the buffer that was active when Loft opened |\n| `LoftMarkedBuffer`     | `DiffAdd`        | Full line — marked / pinned buffers                     |\n| `LoftMark`             | `DiagnosticInfo` | The `(✓)` / `➊`–`➒` mark symbol                         |\n| `LoftCurrentIndicator` | `Statement`      | The `●` current-buffer dot                              |\n| `LoftModified`         | `DiagnosticWarn` | The `[+]` unsaved-changes indicator                     |\n| `LoftBufferNumber`     | `Comment`        | The `{N}` buffer-number token                           |\n\nLine-level groups (`LoftCurrentBuffer`, `LoftMarkedBuffer`) set the background for the\nentire line. Inline groups are layered on top at higher priority, so their foreground\ncolours remain visible against the line background.\n\n### Overriding highlight groups\n\nSet your overrides **after** your colorscheme loads so they are not cleared on theme\nchange:\n\n```lua\n-- Example: make the current-buffer line stand out with a custom colour\nvim.api.nvim_set_hl(0, \"LoftCurrentBuffer\", { bg = \"#264F78\", bold = true })\n\n-- Example: dim the buffer-number column even further\nvim.api.nvim_set_hl(0, \"LoftBufferNumber\", { fg = \"#555555\" })\n```\n\nOr hook into the `ColorScheme` autocmd if you want the override to survive theme switches:\n\n```lua\nvim.api.nvim_create_autocmd(\"ColorScheme\", {\n  callback = function()\n    vim.api.nvim_set_hl(0, \"LoftCurrentBuffer\", { bg = \"#264F78\", bold = true })\n  end,\n})\n```\n\n## Window options\n\nLoft exposes separate configuration tables for the **main UI window** and the **help window**.\nBoth sit inside `require(\"loft\").setup({})`.\n\n### Main window (`window`)\n\n| Option       | Type                        | Default                           | Description                        |\n| ------------ | --------------------------- | --------------------------------- | ---------------------------------- |\n| `width`      | `integer\\|nil`              | 80 % of columns                   | Explicit window width              |\n| `height`     | `integer\\|nil`              | registry size (max 80 % of lines) | Explicit window height             |\n| `row`        | `integer\\|nil`              | centred                           | Absolute row position (0-indexed)  |\n| `col`        | `integer\\|nil`              | centred                           | Absolute col position (0-indexed)  |\n| `row_offset` | `integer`                   | `0`                               | Added to the computed/explicit row |\n| `col_offset` | `integer`                   | `0`                               | Added to the computed/explicit col |\n| `title`      | `string\\|nil`               | auto (Loft name)                  | Custom title; Neovim ≥ 0.9 only    |\n| `title_pos`  | `\"left\"\\|\"center\"\\|\"right\"` | `\"center\"`                        | Title alignment                    |\n| `footer`     | `string\\|nil`               | auto (smart-order indicator)      | Custom footer; Neovim ≥ 0.10 only  |\n| `footer_pos` | `\"left\"\\|\"center\"\\|\"right\"` | `\"center\"`                        | Footer alignment                   |\n| `zindex`     | `integer`                   | `100`                             | Float z-index                      |\n| `border`     | `string\\|string[]`          | `\"rounded\"`                       | Border style                       |\n\n**Positioning example** — pin the window to the bottom of the screen, slightly inset:\n\n```lua\nrequire(\"loft\").setup({\n  window = {\n    row = vim.o.lines - 12,  -- near the bottom\n    col_offset = 4,           -- shift right by 4 columns\n    height = 10,\n    border = \"single\",\n    title = \" my buffers \",\n    title_pos = \"left\",\n  },\n})\n```\n\n### Help window (`help_window`)\n\nThe help window is opened with the `show_help` keymap (default `?`). It inherits the\nmain window's `border` when its own `border` is not set, and its `zindex` is always\nclamped to be greater than the main window's `zindex` so it always floats on top.\n\n| Option       | Type               | Default                            | Description                                        |\n| ------------ | ------------------ | ---------------------------------- | -------------------------------------------------- |\n| `disable`    | `boolean`          | `false`                            | Set `true` to prevent the help window from opening |\n| `width`      | `integer\\|nil`     | `70`                               | Explicit width                                     |\n| `height`     | `integer\\|nil`     | content height (max 80 % of lines) | Explicit height                                    |\n| `row`        | `integer\\|nil`     | centred                            | Absolute row position                              |\n| `col`        | `integer\\|nil`     | centred                            | Absolute col position                              |\n| `row_offset` | `integer`          | `0`                                | Added to the computed/explicit row                 |\n| `col_offset` | `integer`          | `0`                                | Added to the computed/explicit col                 |\n| `border`     | `string\\|string[]` | inherits `window.border`           | Border style                                       |\n| `zindex`     | `integer\\|nil`     | `window.zindex + 10`               | Float z-index (always \u003e main zindex)               |\n\n```lua\nrequire(\"loft\").setup({\n  help_window = {\n    disable = false,\n    border = \"double\",  -- different border from the main window\n    row_offset = -2,    -- shift slightly upward\n  },\n})\n```\n\n## Autocmds\n\nLoft fires the following `User` autocmds:\n\n| Event                       | Description                                        | `ev.data`                                 |\n| --------------------------- | -------------------------------------------------- | ----------------------------------------- |\n| `User LoftBufferMark`       | A buffer was marked or unmarked.                   | `{ buffer: number, mark_state: boolean }` |\n| `User LoftSmartOrderToggle` | Smart order was toggled on or off.                 | `{ smart_order_state: boolean }`          |\n| `User LoftRegistryChanged`  | The registry mutated (add, remove, reorder, mark). | _(no data)_                               |\n| `User LoftBufferSwitch`     | Loft navigated to a buffer (next/prev/marked/alt). | `{ buffer: number, source: string }`      |\n\nThe `source` field in `LoftBufferSwitch` is one of: `\"next\"`, `\"prev\"`, `\"marked_next\"`, `\"marked_prev\"`, `\"alt\"`.\n\nExample — redraw statusline on any registry change or buffer navigation:\n\n```lua\nvim.api.nvim_create_autocmd(\"User\", {\n  pattern = { \"LoftRegistryChanged\", \"LoftBufferSwitch\", \"LoftSmartOrderToggle\", \"LoftBufferMark\" },\n  callback = function()\n    vim.cmd(\"redrawstatus\")\n  end,\n})\n```\n\n## Tips\n\nIf you think bufferline sucks and prefer working with the info in statusline like me then you can show the smart order and marked info in your statusline.\n\nGet the info from Loft's [public API](#public-api) (`smart_order_indicator()` and `get_buffer_mark()`) and infuse like so:\n\n```lua\nvim.api.nvim_set_hl(0, \"MiniStatuslineFilename\", { fg = \"#FFD700\", bg = \"#262D43\", bold = true })\nvim.api.nvim_set_hl(0, \"StatusLineLoftSmartOrder\", { fg = \"#ffffff\", bg = \"#005f87\", bold = true })\nlocal smart_order_status = \"%#StatusLineLoftSmartOrder#\" .. require(\"loft.ui\"):smart_order_indicator()\nlocal buffer_mark = require(\"loft.ui\"):get_buffer_mark()\nlocal filename = MiniStatusline.section_filename({ trunc_width = 140 })\nMiniStatusline.combine_groups({\n  -- ...\n  { hl = \"StatusLineLoftSmartOrder\", strings = { smart_order_status } },\n  \"%\u003c\",\n  -- ...\n  { hl = \"MiniStatuslineFilename\", strings = { filename .. buffer_mark } },\n  \"%=\",\n  -- ...\n})\n```\n\nThen listen for Loft's user autocmds and redraw your statusline:\n\n```lua\nvim.api.nvim_create_autocmd(\"User\", {\n  pattern = { \"LoftSmartOrderToggle\", \"LoftBufferMark\", \"LoftRegistryChanged\", \"LoftBufferSwitch\" },\n  callback = function()\n    vim.cmd(\"redrawstatus\")\n  end,\n})\n```\n\nHere's what your statusline would look like:\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"assets/statusline_showcase.png\" alt=\"Statusline Showcase\" /\u003e\n\u003c/div\u003e\n\u003cbr /\u003e\n\nAnother little tip: you can search (/) the loft UI by the ID of filename of the entry/buffer to get it.\n\n### Usage with oil.nvim\n\nIf you use [`oil.nvim`](https://github.com/stevearc/oil.nvim) (which I recommend for file exploration, btw), you can hook into the `OilActionsPost` user autocmd to clean the Loft registry after file deletion:\n\n```lua\nvim.api.nvim_create_autocmd(\"User\", {\n  pattern = \"OilActionsPost\",\n  desc = \"Clean Loft registry after oil.nvim deletions\",\n  callback = function(args)\n    local actions = args.data and args.data.actions or {}\n    local needs_clean = false\n    for _, action in ipairs(actions) do\n      if action.type == \"delete\" or action.type == \"trash\" then\n        needs_clean = true\n        break\n      end\n    end\n    if needs_clean then\n      local ok, registry = pcall(require, \"loft.registry\")\n      if ok then\n        registry:clean()\n      end\n    end\n  end,\n})\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to check out the [contribution guide](./CONTRIBUTING.md).\n\n## Public API\n\nThese methods are available on the UI singleton (`require(\"loft.ui\")`) for use in\nstatuslines and other integrations. They are **stable** and safe to call at any time\n(they return empty strings when Loft has not been set up yet).\n\n### `UI:get_buffer_mark([buffer]) → string`\n\nReturns the mark symbol for a buffer, or `\"\"` if the buffer is not marked.\n\n| Parameter | Type           | Description                                        |\n| --------- | -------------- | -------------------------------------------------- |\n| `buffer`  | `integer\\|nil` | Buffer number to query; defaults to current buffer |\n\n**Returns** `string` — one of:\n\n- `\"\"` — buffer is not marked\n- `\"(✓)\"` — marked, no keymap slot assigned\n- `\"➊(✓)\"` – `\"➒(✓)\"` — marked with a numbered keymap slot (`show_marked_mapping_num = true`)\n\nThe number prefix style (`solid` vs `outline`) follows the `marked_mapping_num_style` config.\n\n### `UI:smart_order_indicator() → string`\n\nReturns the smart-order indicator string, or `\"\"` when smart order is off.\n\n**Returns** `string` — `\"⟅⇅⟆\"` when smart order is enabled, `\"\"` otherwise.\n\n**Statusline example** (mini.statusline):\n\n```lua\nlocal loft_ui = require(\"loft.ui\")\n\n-- In your statusline build function:\nlocal smart_order_status = loft_ui:smart_order_indicator()\nlocal buffer_mark        = loft_ui:get_buffer_mark()\n\n-- Redraw on any Loft event\nvim.api.nvim_create_autocmd(\"User\", {\n  pattern = { \"LoftSmartOrderToggle\", \"LoftBufferMark\", \"LoftRegistryChanged\", \"LoftBufferSwitch\" },\n  callback = function() vim.cmd(\"redrawstatus\") end,\n})\n```\n\n## Roadmap\n\n- **In-UI fuzzy filter** — A keymap (e.g. `f`) to enter a live filter prompt inside the Loft window; entries are narrowed in-place by filename/path, making the UI practical in very large buffer lists.\n\n- **Tab-local registries** — An option for each tab page to maintain its own independent buffer registry, supporting project-separation workflows across tabs.\n\n- **Lualine / statusline component** — A first-class `require(\"loft.lualine\")` module that returns a ready-made lualine component (and a plain statusline string for other statusline plugins) showing the smart-order indicator and current-buffer mark without requiring users to wire up the autocmds and API calls manually.\n\n- **Telescope extension** — A `require(\"telescope\").extensions.loft` picker that surfaces the Loft registry as a Telescope results list, enabling fuzzy search, preview, and all Telescope actions (select, delete, mark) over your tracked buffers.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamgideonidoko%2Floft.nvim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiamgideonidoko%2Floft.nvim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamgideonidoko%2Floft.nvim/lists"}