{"id":49025079,"url":"https://github.com/mjmjm0101/quickui.nvim","last_synced_at":"2026-04-27T09:03:40.795Z","repository":{"id":349446880,"uuid":"1202110606","full_name":"mjmjm0101/quickui.nvim","owner":"mjmjm0101","description":"Not just a UI plugin — manage the cognitive load of your own Neovim setup","archived":false,"fork":false,"pushed_at":"2026-04-18T22:07:56.000Z","size":258,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-20T22:03:06.018Z","etag":null,"topics":["cognitive-layer","developer-tools","keybindings","lua","menu","neovim","neovim-plugin","productivity","quickui","tui","ui","workflow"],"latest_commit_sha":null,"homepage":"https://zenn.dev/mjmjm0101","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/mjmjm0101.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":"2026-04-05T16:00:29.000Z","updated_at":"2026-04-18T22:07:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mjmjm0101/quickui.nvim","commit_stats":null,"previous_names":["mjmjm0101/quickui.nvim"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mjmjm0101/quickui.nvim","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjmjm0101%2Fquickui.nvim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjmjm0101%2Fquickui.nvim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjmjm0101%2Fquickui.nvim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjmjm0101%2Fquickui.nvim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mjmjm0101","download_url":"https://codeload.github.com/mjmjm0101/quickui.nvim/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjmjm0101%2Fquickui.nvim/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32329467,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["cognitive-layer","developer-tools","keybindings","lua","menu","neovim","neovim-plugin","productivity","quickui","tui","ui","workflow"],"created_at":"2026-04-19T06:00:43.162Z","updated_at":"2026-04-27T09:03:40.777Z","avatar_url":"https://github.com/mjmjm0101.png","language":"Lua","funding_links":[],"categories":["UI"],"sub_categories":["OS-specific"],"readme":"# quickui.nvim\n\n\u003e It provides a UI — but the goal isn't UI.  \n\u003e It's about managing the cognitive load of your own Neovim setup, so nothing gets lost.\n\n![English concept](https://github.com/user-attachments/assets/f5985d92-0526-4113-9a72-3ea4a00f9cd2)\n\nLightweight, keyboard-friendly TUI-style menus for Neovim (menubar + context menus)\n\n- **Menubar** — a title strip at the top of the editor with a dropdown that opens below the selected title\n- **Context menus** — floating popups positioned at the cursor (normal and visual mode)\n- Nested submenus (multi-level)\n- Per-item conditions (`conditions`) and filetype filters (`ft`)\n- Fully configurable keybindings\n- Global keybindings are suppressed inside quickui buffers by default (configurable via `suppress_all_keys`)\n- [Fuzzy finder integration (Telescope, fzf-lua, snacks, mini.pick)](#fuzzy-finder-integration)\n\nThis plugin provides similar functionality to [skywind3000/vim-quickui](https://github.com/skywind3000/vim-quickui) and [nvzone/menu](https://github.com/nvzone/menu). Both were a great source of inspiration, and I'm grateful to their authors.\n\n---\n\n## Requirements\n\n- Neovim 0.10+\n\n---\n\n\n## Demo\n\n### Menubar\n\nMenubar with nested submenus:\n\n![Menubar demo](https://github.com/user-attachments/assets/89e5229e-65c6-4f5b-978f-0d37ec69e0bb)\n\n### Context Menu\n\nContext menu at cursor position:\n\n![Context menu demo](https://github.com/user-attachments/assets/05e99d4f-959b-4ec5-ad62-4de233f86a2a)\n\n## 🧠 Why quickui.nvim?\n\nModern Neovim setups often include dozens of plugins,\nbut their commands and keybindings are fragmented and hard to manage.\n\nquickui.nvim provides a structured UI — not for recalling or searching,\nbut for organizing your tools by meaning.\n\nSearch-based workflows work well when you already remember what exists.\nBut for infrequent actions, what matters more is having a structure you can navigate.\n\nDo you have plugins that seemed useful when you installed them,\nbut ended up unused because you couldn’t remember how to use them?\n\nquickui.nvim makes those features accessible again by turning them into something you can navigate.\n\nIt complements the traditional “memorize commands” workflow\nby adding a new approach: organizing capabilities.\n\nAs a result, your menus can act as a portable UI layer — staying consistent\neven when switching between setups like LazyVim, NvChad, or your own config.\n\n## Installation\n\n### [lazy.nvim](https://github.com/folke/lazy.nvim)\n\n```lua\n{\n  \"mjmjm0101/quickui.nvim\",\n  lazy = false,\n  config = function()\n    require(\"quickui\").setup({})\n  end,\n}\n```\n\n### [packer.nvim](https://github.com/wbthomason/packer.nvim)\n\n```lua\nuse {\n  \"mjmjm0101/quickui.nvim\",\n  config = function()\n    require(\"quickui\").setup({})\n  end,\n}\n```\n\n### [vim-plug](https://github.com/junegunn/vim-plug)\n\n```vim\nPlug 'mjmjm0101/quickui.nvim'\n\nlua require(\"quickui\").setup({})\n```\n\n---\n\n## Quick Start\n\n\n```lua\nrequire(\"quickui\").setup({\n  keymap = \"\u003cSpace\u003e\",  -- toggle the menubar\n  border = \"single\",\n\n  menus = {\n    {\n      name  = \"\u0026File\",\n      items = {\n        { name = \"\u0026New\",   cmd = \":enew\u003cCR\u003e\", key = \"\u003cC-n\u003e\" },\n        { name = \"\u0026Open\",  cmd = \":e \",       key = \"\u003cC-o\u003e\" },\n        { name = \"\u0026Save\",  cmd = \":w\u003cCR\u003e\",    key = \"\u003cC-s\u003e\" },\n        { name = \"separator\" },\n        { name = \"\u0026Quit\",  cmd = \":qa\u003cCR\u003e\",   key = \"\u003cC-q\u003e\", rtxt = \"Ctrl-q\" },\n      },\n    },\n    {\n      name  = \"\u0026Edit\",\n      items = {\n        { name = \"\u0026Undo\",  cmd = \"u\",      key = \"\u003cC-z\u003e\" },\n        { name = \"\u0026Redo\",  cmd = \"\u003cC-r\u003e\",  key = \"\u003cC-y\u003e\", rtxt = \"Ctrl-y\" },\n        { name = \"\u0026Copy\",  cmd = '\"+y',    key = \"\u003cC-c\u003e\" },\n        { name = \"\u0026Paste\", cmd = '\"+p',    key = \"\u003cC-v\u003e\" },\n      },\n    },\n  },\n})\n```\n\nOr try the sample\n\n```lua\nrequire(\"quickui-sample\")\n```\n\nThe sample includes:\n- `context/normal.lua` — normal-mode context menu (LSP, diagnostics, edit, filetype-specific items)\n- `context/visual.lua` — visual-mode context menu (case conversion, clipboard, indent, sort, LSP range)\n- `context/snacks_explorer.lua` — context menu for [snacks.nvim](https://github.com/folke/snacks.nvim) explorer (open, new, rename, delete, copy path); see the file header for setup instructions\n\n\n\u003e The sample is only a starting point.  \n\u003e quickui.nvim becomes truly useful when you organize your own setup in your own structure — not someone else's.\n\n---\n\n## Configuration Reference\n\n```lua\nrequire(\"quickui\").setup({\n\n  -- Key to toggle the menubar (default: \"\u003cSpace\u003e\")\n  keymap = \"\u003cSpace\u003e\",\n\n  -- Border style: \"none\" | \"single\" | \"double\" | \"rounded\" | \"dotted\" | \"dashed\"\n  -- Note: \"none\" hides border characters but still reserves their cell width\n  -- to prevent layout shifts. Zero-thickness borders are not supported.\n  border = \"single\",\n\n  -- Transparency 0-100. number = both bar and menu, table = individual\n  -- Defaults: bar = 0, menu = 40\n  winblend = { bar = 0, menu = 40 },\n\n  -- Number of padding spaces on each side of a menubar item (default: 1)\n  menubar_padding = 2,\n\n  -- Separator character between menubar items. \"\" to disable (default: \"│\")\n  menubar_separator = \"│\",\n\n  -- Scroll indicator shown when menus overflow the screen width (defaults: \"\u003c\" / \"\u003e\")\n  menubar_indicator_left  = \"\u003c\",\n  menubar_indicator_right = \"\u003e\",\n\n  -- Icon displayed at the right edge of a submenu item (default: \"›\")\n  submenu_icon = \"›\",\n\n  -- When true, render `key` in a dedicated column with QuickUIMenuKey highlight,\n  -- separate from `rtxt`. When false, `key` falls back to the rtxt column when\n  -- `rtxt` is not set (default: false)\n  showkeys = false,\n\n  -- Highlight group overrides\n  highlights = {\n    accent            = \"#89b4fa\",  -- shortcut letter (character after \u0026)\n    rtxt              = \"#a6e3a1\",  -- right-aligned text (rtxt field)\n    key               = \"#f9e2af\",  -- key column when showkeys = true\n    menu              = { bg = \"#1e1e2e\", fg = \"#cdd6f4\" },\n    menu_sel          = { bg = \"#4974aa\", fg = \"#cdd6f4\" },\n    menu_border       = { fg = \"#89b4fa\" },\n    menubar           = { bg = \"#181825\", fg = \"#cdd6f4\" },\n    menubar_sel       = { bg = \"#313244\", fg = \"#89b4fa\" },\n    menubar_separator = { fg = \"#585b70\", bg = \"#181825\" },\n    menubar_indicator = { fg = \"#f38ba8\", bold = true },\n  },\n\n  -- Keymap overrides (merged on top of defaults)\n  keymaps = {\n    up        = { \"k\", \"\u003cUp\u003e\" },\n    down      = { \"j\", \"\u003cDown\u003e\" },\n    exec      = { \"\u003cCR\u003e\" },           -- execute item (opens submenu if item has one)\n    close     = { \"\u003cEsc\u003e\", \"q\" },\n    submenu   = { \"\u003cTab\u003e\" },             -- open submenu\n    back      = { \"\u003cBS\u003e\", \"\u003cS-Tab\u003e\" },   -- close submenu and return to parent\n    menu_prev = { \"h\" },                 -- menubar: move to previous menu\n    menu_next = { \"l\" },                 -- menubar: move to next menu\n    nav_prev  = { \"\u003cLeft\u003e\" },            -- context-sensitive: close submenu or prev menu\n    nav_next  = { \"\u003cRight\u003e\" },           -- context-sensitive: open submenu or next menu\n    mouse     = { \"\u003cLeftMouse\u003e\" },\n  },\n\n  -- If true, start from an empty keymap set (only user-defined keys are active)\n  disable_default_keymaps = false,\n\n  -- When true (default), map all keys to \u003cNop\u003e in plugin buffers to block\n  -- global keymaps. Set to false to leave global keymaps untouched.\n  suppress_all_keys = true,\n\n  -- When true (default), remember the last open top-level menu and scroll\n  -- position and restore them when the menubar is reopened.\n  -- When false, always start from the leftmost menu.\n  menubar_restore = true,\n\n  -- List of top-level menu specs (see Menu Definition below)\n  menus = { ... },\n})\n```\n\n---\n\n## Default Keybindings\n\n| Action                        | Keys                         |\n|-------------------------------|------------------------------|\n| Move up                       | `k` / `\u003cUp\u003e`                 |\n| Move down                     | `j` / `\u003cDown\u003e`               |\n| Execute                       | `\u003cCR\u003e`                       |\n| Close                         | `\u003cEsc\u003e` / `q`                |\n| Open submenu                  | `\u003cTab\u003e`                      |\n| Close submenu / back to parent| `\u003cBS\u003e` / `\u003cS-Tab\u003e`           |\n| Menubar: previous menu        | `h`                          |\n| Menubar: next menu            | `l`                          |\n| Close submenu / prev menu     | `\u003cLeft\u003e`                     |\n| Open submenu / next menu      | `\u003cRight\u003e`                    |\n| Mouse click                   | `\u003cLeftMouse\u003e`                |\n| Shortcut key (`\u0026`)            | Character after `\u0026` in name  |\n| Shortcut key (`key`)          | Value of the `key` item field |\n\n---\n\n## Menu Definition\n\n### Top-level menu spec\n\nPass a list of specs to `setup()` via `menus`, or register dynamically with `require(\"quickui\").menu_install()`.\n\n```lua\n{\n  name       = \"\u0026File\",        -- \u0026 marks the shortcut character\n  priority   = 100,            -- display order, ascending left-to-right (default: 100)\n  conditions = function(opt)   -- nil = always shown, false = hidden\n    return true\n  end,\n  items = { ... },             -- item list, or function(opt) → list\n}\n```\n\nNames prefixed with `\u0026@` or `@` default to `priority = 10000` and are sorted to the far right.\n\n### Item fields\n\n```lua\nitems = {\n  -- key only: \"\u003cC-s\u003e\" is shown as the right-aligned hint and triggers the item\n  { name = \"\u0026Save\",  cmd = \":w\u003cCR\u003e\",  key = \"\u003cC-s\u003e\" },\n\n  -- rtxt overrides key display (key still works as a binding)\n  { name = \"\u0026Save As...\", cmd = \":saveas \", key = \"\u003cC-shift-s\u003e\", rtxt = \"Ctrl-Shift-S\" },\n\n  -- rtxt=\"\" suppresses display; key is still active\n  { name = \"\u0026Close\", cmd = \":bd\u003cCR\u003e\", key = \"\u003cC-w\u003e\", rtxt = \"\" },\n\n  -- rtxt only: display hint with no in-menu keybinding\n  { name = \"\u0026Redo\",  cmd = \"\u003cC-r\u003e\",   rtxt = \"Ctrl-R\" },\n\n  -- Separator\n  { name = \"separator\" },\n\n  -- Submenu\n  {\n    name  = \"\u0026Recent\",\n    items = {\n      { name = \"file1.txt\", cmd = \":e file1.txt\u003cCR\u003e\" },\n      { name = \"file2.txt\", cmd = \":e file2.txt\u003cCR\u003e\" },\n    },\n  },\n\n  -- Function command\n  { name = \"\u0026Grep\", cmd = function(opt)\n    vim.ui.input({ prompt = \"Pattern: \" }, function(input)\n      if input then vim.cmd(\"grep \" .. input) end\n    end)\n  end },\n\n  -- Conditional display\n  { name = \"Laravel \u0026Artisan\", cmd = \":!php artisan\",\n    conditions = function(opt)\n      return vim.fn.filereadable(\"artisan\") == 1\n    end },\n\n  -- Filetype filter (comma-separated)\n  { name = \"Validate \u0026HTML\", cmd = \":!tidy -errors %\",\n    ft = \"html,xml\" },\n\n  -- Per-item highlight\n  { name = \"Danger Zone\", cmd = \"...\", hl = \"ErrorMsg\" },\n}\n```\n\n| Field        | Type               | Description                                                              |\n|--------------|--------------------|--------------------------------------------------------------------------|\n| `name`       | string             | Display name. `\u0026X` sets the shortcut key. `%{expr}` is evaluated at open time. |\n| `cmd`        | string \\| function | Command to run. Strings are fed via `feedkeys`; functions receive `opt`. |\n| `key`        | string             | Keybinding active while the menu is open (e.g. `\"\u003cC-s\u003e\"`). When `showkeys = false` (default), falls back to the rtxt column if `rtxt` is not set. When `showkeys = true`, rendered in its own column with `QuickUIMenuKey` highlight. Submenu items always use the kmap; the column itself is suppressed for them. |\n| `rtxt`       | string             | Right-aligned text. Overrides `key` display when specified. Set to `\"\"` to suppress display even if `key` is set. Supports `%{expr}` evaluation (same as `name`). |\n| `items`      | table              | Sub-item list — presence makes this item a submenu trigger.              |\n| `conditions` | bool \\| function   | `false` hides the item. Function receives `opt`, return `false` to hide. |\n| `ft`         | string             | Comma-separated filetypes. Item is hidden when the current ft doesn't match. |\n| `hl`         | string             | Highlight group applied to the item row.                                 |\n\nThe `opt` table passed to `cmd` and `conditions` functions:\n\n| Key         | Type       | Always present | Description                                                          |\n|-------------|------------|----------------|----------------------------------------------------------------------|\n| `filetype`  | string     | ✓              | `vim.bo.filetype` at the time the menu was opened                    |\n| `cwd`       | string     | ✓              | `vim.fn.getcwd()` at the time the menu was opened                    |\n| `selection` | table\\|nil | context_visual only | Visual selection: `{ text, lines, mode }`                       |\n| _…data_     | any        | when provided  | All fields from the `data` argument of `context_normal` / `context_visual` |\n\n---\n\n## API\n\n### `require(\"quickui\").setup(opts)`\n\nInitialize the plugin. See the Configuration Reference above for all options.\n\n### `require(\"quickui\").menu_install(spec)`\n\nRegister or replace a top-level menu at runtime. An existing menu with the same name is replaced.\n\n```lua\nrequire(\"quickui\").menu_install({\n  name  = \"\u0026Debug\",\n  items = { ... },\n})\n```\n\n### `require(\"quickui\").context_normal(spec, data)`\n\nOpen a context menu at the cursor position (normal mode).\n\n```lua\n-- Plain item list\nrequire(\"quickui\").context_normal({\n  { name = \"Copy\",  cmd = '\"+y' },\n  { name = \"Paste\", cmd = '\"+p' },\n})\n\n-- items as a function (dynamic generation)\nrequire(\"quickui\").context_normal({\n  items = function(opt)\n    return {\n      { name = \"Filetype: \" .. opt.filetype, cmd = \"\" },\n    }\n  end,\n})\n\n-- Pass arbitrary data through to cmd functions\nrequire(\"quickui\").context_normal(ctx, { target = some_item })\n-- cmd = function(opt)  →  opt.target == some_item\n```\n\n### `require(\"quickui\").context_visual(spec, data)`\n\nOpen a context menu at the cursor position (visual mode).\nThe visual selection is highlighted while the menu is open.\n`opt.selection` is set automatically.\n\n```lua\nrequire(\"quickui\").context_visual({\n  items = function(opt)\n    return {\n      { name = \"Selection: \" .. opt.selection.text, cmd = \"\" },\n      { name = \"\u0026Uppercase\", cmd = function(opt)\n          -- opt.selection.text  — selected text (joined with \\n)\n          -- opt.selection.lines — table of selected lines\n          -- opt.selection.mode  — \"v\" | \"V\" | \"\\22\" (block)\n        end },\n    }\n  end,\n})\n```\n\n---\n\n## Context Menu Example\n\n```lua\n-- lua/plugins/quickui.lua\nlocal quickui = require(\"quickui\")\n\nlocal ctx = {\n  items = function(opt)\n    return {\n      { name = \"\u0026Format\",      cmd = function() vim.lsp.buf.format() end },\n      { name = \"\u0026Code Action\", cmd = function() vim.lsp.buf.code_action() end },\n      { name = \"separator\" },\n      { name = \"\u0026Yank All\",    cmd = \":%y+\u003cCR\u003e\" },\n      -- shown only from context_visual\n      -- shown only from context_visual (gv re-enters the last visual selection)\n      { name = \"\u0026Uppercase\",   cmd = \"gvU\",\n        conditions = function(opt) return opt.selection ~= nil end },\n      { name = \"\u0026Lowercase\",   cmd = \"gvu\",\n        conditions = function(opt) return opt.selection ~= nil end },\n    }\n  end,\n}\n\n-- Normal mode\nvim.keymap.set(\"n\", \"\u003cTab\u003e\", function()\n  quickui.context_normal(ctx)\nend, { noremap = true, silent = true })\n\n-- Visual mode (selection is highlighted; opt.selection is set automatically)\nvim.keymap.set(\"x\", \"\u003cTab\u003e\", function()\n  quickui.context_visual(ctx)\nend, { noremap = true, silent = true })\n```\n\n---\n\n## Highlight Groups\n\n| Group                      | Default link     | Target                               |\n|----------------------------|------------------|--------------------------------------|\n| `QuickUIMenubar`           | `StatusLine`     | Menubar background                   |\n| `QuickUIMenubarSel`        | `PmenuSel`       | Selected menubar item                |\n| `QuickUIMenubarSeparator`  | `NonText`        | Menubar separator character          |\n| `QuickUIMenubarIndicator`  | `WarningMsg`     | Scroll indicator (`\u003c` / `\u003e`)         |\n| `QuickUIMenu`              | `Normal`         | Dropdown / popup background          |\n| `QuickUIMenuBorder`        | `FloatBorder`    | Window border                        |\n| `QuickUIMenuSel`           | `PmenuSel`       | Selected item row                    |\n| `QuickUIMenuAccent`        | `Special`        | Shortcut character (after `\u0026`)       |\n| `QuickUIMenuRtxt`          | `Special`        | Right-aligned text (`rtxt` field, or `key` fallback) |\n| `QuickUIMenuKey`           | `Special`        | `key` column when `showkeys = true`  |\n| `QuickUIVisualSel`         | `Visual`         | Visual selection overlay (context_visual) |\n\n---\n\n## Fuzzy Finder Integration\n\nquickui.nvim menus can be exposed as searchable data,\nmaking it possible to use fuzzy finders as an entry point to your structured UI.\n\nInstead of relying only on remembering commands or guessing search terms,\nyou can search within your own curated structure.\n\nSearch results reflect your menu hierarchy and naming,\nso you can find actions based on how you think about them — not how plugins name them.\n\n- [telescope-quickui.nvim](https://github.com/mjmjm0101/telescope-quickui.nvim)\n- [fzf-lua-quickui.nvim](https://github.com/mjmjm0101/fzf-lua-quickui.nvim)\n- [snacks-picker-quickui.nvim](https://github.com/mjmjm0101/snacks-picker-quickui.nvim)\n- [mini-pick-quickui.nvim](https://github.com/mjmjm0101/mini-pick-quickui.nvim)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmjmjm0101%2Fquickui.nvim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmjmjm0101%2Fquickui.nvim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmjmjm0101%2Fquickui.nvim/lists"}