feat: add configurable floating window support for memo list

- Added `window` configuration options (enable_float, width, height, border).
- Implemented LazyVim-style floating window logic in UI.
- Updated README (EN/CN) and help docs with new configuration examples.
This commit is contained in:
Elflare
2025-11-29 14:11:38 +08:00
parent 052120b726
commit b4a381c52c
4 changed files with 161 additions and 98 deletions

View File

@@ -11,6 +11,7 @@ A Neovim plugin to interact with [Memos](https://github.com/usememos/memos) righ
- **Delete Memos**: Delete memos directly from the list. - **Delete Memos**: Delete memos directly from the list.
- **Customizable**: Configure API endpoints, keymaps, and more. - **Customizable**: Configure API endpoints, keymaps, and more.
- **First-time Setup**: On first launch, you will be prompted to enter your Memos host and token. You can choose to save these permanently. - **First-time Setup**: On first launch, you will be prompted to enter your Memos host and token. You can choose to save these permanently.
- **Floating Window**: Optional LazyVim-style floating window for the memo list.
## 📦 Installation ## 📦 Installation
@@ -75,6 +76,13 @@ require("memos").setup({
-- Auto-save the memo when leaving insert mode or holding the cursor. -- Auto-save the memo when leaving insert mode or holding the cursor.
auto_save = false, auto_save = false,
-- Window configuration
window = {
enable_float = false, -- Set to true to open the list in a floating window
width = 0.85, -- Width ratio (0.0 to 1.0)
height = 0.85, -- Height ratio (0.0 to 1.0)
border = "rounded", -- Border style: "single", "double", "rounded", "solid", "shadow"
},
-- Set to false or nil to disable a keymap -- Set to false or nil to disable a keymap
keymaps = { keymaps = {
@@ -117,6 +125,7 @@ require("memos").setup({
- **删除 Memos**: 直接从列表中删除 memo。 - **删除 Memos**: 直接从列表中删除 memo。
- **可定制**: 可配置 API 地址、快捷键等。 - **可定制**: 可配置 API 地址、快捷键等。
- **首次启动引导**: 首次启动时会提示输入 Memos 的 host 和 token并询问是否永久保存。 - **首次启动引导**: 首次启动时会提示输入 Memos 的 host 和 token并询问是否永久保存。
- **浮动窗口**: 可选的 LazyVim 风格浮动窗口来展示 memo 列表。
## 📦 安装 ## 📦 安装
@@ -181,6 +190,13 @@ require("memos").setup({
-- 当离开插入模式或光标静止时,自动保存 memo。 -- 当离开插入模式或光标静止时,自动保存 memo。
auto_save = false, auto_save = false,
-- 窗口配置
window = {
enable_float = false, -- 设置为 true 以在浮动窗口中打开列表
width = 0.85, -- 宽度比例 (0.0 到 1.0)
height = 0.85, -- 高度比例 (0.0 到 1.0)
border = "rounded", -- 边框样式: "single", "double", "rounded", "solid", "shadow"
},
-- 设置为 false 或 nil 可以禁用某个快捷键 -- 设置为 false 或 nil 可以禁用某个快捷键
keymaps = { keymaps = {

View File

@@ -43,6 +43,13 @@ require("memos").setup({
-- Number of memos to fetch per page -- Number of memos to fetch per page
pageSize = 50, pageSize = 50,
auto_save = false, auto_save = false,
-- Window configuration
window = {
enable_float = false, -- Set to true to open the list in a floating window
width = 0.85, -- Width ratio (0.0 to 1.0)
height = 0.85, -- Height ratio (0.0 to 1.0)
border = "rounded", -- Border style: "single", "double", "rounded", "solid", "shadow"
},
-- Set to false or nil to disable a keymap -- Set to false or nil to disable a keymap
keymaps = { keymaps = {
-- Keymap to open the memos list. Default: <leader>mm -- Keymap to open the memos list. Default: <leader>mm

View File

@@ -1,29 +1,36 @@
local M = {} local M = {}
M.config = { M.config = {
host = nil, host = nil,
token = nil, token = nil,
auto_save = false, auto_save = false,
page_size = 50, page_size = 50,
keymaps = { -- 【新增】窗口配置
start_memos = "<leader>mm", window = {
-- 在列表窗口中的快捷键 enable_float = false, -- 默认为 false设为 true 则开启浮动窗口
list = { width = 0.85, -- 宽度占屏幕比例
add_memo = 'a', height = 0.85, -- 高度占屏幕比例
delete_memo = 'd', border = "rounded", -- 边框样式: "single", "double", "rounded", "solid", "shadow"
delete_memo_visual = 'dd', },
edit_memo = '<CR>', keymaps = {
vsplit_edit_memo = '<Tab>', start_memos = "<leader>mm",
search_memos = 's', -- 在列表窗口中的快捷键
refresh_list = 'r', list = {
next_page = '.', add_memo = "a",
quit = 'q' delete_memo = "d",
}, delete_memo_visual = "dd",
-- 在编辑和创建窗口中的快捷键 edit_memo = "<CR>",
buffer = { vsplit_edit_memo = "<Tab>",
save = '<leader>ms' search_memos = "s",
} refresh_list = "r",
} next_page = ".",
quit = "q",
},
-- 在编辑和创建窗口中的快捷键
buffer = {
save = "<leader>ms",
},
},
} }
local config_dir = vim.fn.stdpath("data") .. "/memos.nvim" local config_dir = vim.fn.stdpath("data") .. "/memos.nvim"
@@ -31,96 +38,96 @@ local config_file_path = config_dir .. "/memos_config.json"
-- 【修改】全新的、能处理 host 和 token 的交互式配置函数 -- 【修改】全新的、能处理 host 和 token 的交互式配置函数
local function prompt_for_config() local function prompt_for_config()
local function prompt_for_token() local function prompt_for_token()
vim.ui.input({ vim.ui.input({
prompt = "Memos Access Token:", prompt = "Memos Access Token:",
hide = true hide = true,
}, function(token) }, function(token)
if token and token ~= "" then if token and token ~= "" then
M.config.token = token M.config.token = token
local choice = vim.fn.confirm("Save host and token for future sessions?", "&Yes\n&No", 2) local choice = vim.fn.confirm("Save host and token for future sessions?", "&Yes\n&No", 2)
if choice == 1 then if choice == 1 then
vim.fn.mkdir(config_dir, "p") vim.fn.mkdir(config_dir, "p")
-- 将 host 和 token 一起存入 JSON 文件 -- 将 host 和 token 一起存入 JSON 文件
local config_to_save = { local config_to_save = {
host = M.config.host, host = M.config.host,
token = M.config.token token = M.config.token,
} }
vim.fn.writefile({vim.json.encode(config_to_save)}, config_file_path) vim.fn.writefile({ vim.json.encode(config_to_save) }, config_file_path)
vim.notify("Host and token saved permanently.", vim.log.levels.INFO) vim.notify("Host and token saved permanently.", vim.log.levels.INFO)
end end
else else
vim.notify("No token entered. Memos plugin will not work.", vim.log.levels.ERROR) vim.notify("No token entered. Memos plugin will not work.", vim.log.levels.ERROR)
end end
end) end)
end end
if not M.config.host then if not M.config.host then
vim.ui.input({ vim.ui.input({
prompt = "Memos Host URL (e.g., http://127.0.0.1:5230):" prompt = "Memos Host URL (e.g., http://127.0.0.1:5230):",
}, function(host) }, function(host)
if host and host ~= "" then if host and host ~= "" then
M.config.host = host M.config.host = host
-- 获取到 host 后,接着获取 token -- 获取到 host 后,接着获取 token
prompt_for_token() prompt_for_token()
else else
vim.notify("No host entered. Memos plugin will not work.", vim.log.levels.ERROR) vim.notify("No host entered. Memos plugin will not work.", vim.log.levels.ERROR)
end end
end) end)
else else
-- 如果 host 已存在,只获取 token -- 如果 host 已存在,只获取 token
prompt_for_token() prompt_for_token()
end end
end end
function M.setup(opts) function M.setup(opts)
-- 1. 先加载默认配置 -- 1. 先加载默认配置
local final_config = vim.deepcopy(M.config) local final_config = vim.deepcopy(M.config)
-- 2. 加载文件中的配置 -- 2. 加载文件中的配置
if vim.fn.filereadable(config_file_path) == 1 then if vim.fn.filereadable(config_file_path) == 1 then
local file_content = vim.fn.readfile(config_file_path) local file_content = vim.fn.readfile(config_file_path)
if file_content and #file_content > 0 and file_content[1] ~= "" then if file_content and #file_content > 0 and file_content[1] ~= "" then
local saved_config = vim.json.decode(file_content[1]) local saved_config = vim.json.decode(file_content[1])
final_config = vim.tbl_deep_extend("force", final_config, saved_config) final_config = vim.tbl_deep_extend("force", final_config, saved_config)
end end
end end
-- 3. 加载环境变量 (优先级高于文件) -- 3. 加载环境变量 (优先级高于文件)
local token_from_env = os.getenv('MEMOS_TOKEN') local token_from_env = os.getenv("MEMOS_TOKEN")
if token_from_env and token_from_env ~= "" then if token_from_env and token_from_env ~= "" then
final_config.token = token_from_env final_config.token = token_from_env
end end
local host_from_env = os.getenv('MEMOS_HOST') local host_from_env = os.getenv("MEMOS_HOST")
if host_from_env and host_from_env ~= "" then if host_from_env and host_from_env ~= "" then
final_config.host = host_from_env final_config.host = host_from_env
end end
-- 4. 加载用户在 setup() 中直接提供的配置 (优先级最高) -- 4. 加载用户在 setup() 中直接提供的配置 (优先级最高)
final_config = vim.tbl_deep_extend("force", final_config, opts or {}) final_config = vim.tbl_deep_extend("force", final_config, opts or {})
M.config = final_config M.config = final_config
end end
-- 【修改】确保在执行任何操作前,配置是完整的 -- 【修改】确保在执行任何操作前,配置是完整的
local function ensure_config(callback) local function ensure_config(callback)
if M.config.host and M.config.token then if M.config.host and M.config.token then
callback() callback()
else else
prompt_for_config() prompt_for_config()
end end
end end
function M.create_memo() function M.create_memo()
ensure_config(function() ensure_config(function()
require('memos.ui').create_memo_in_buffer() require("memos.ui").create_memo_in_buffer()
end) end)
end end
function M.show_list() function M.show_list()
ensure_config(function() ensure_config(function()
require('memos.ui').show_memos_list() require("memos.ui").show_memos_list()
end) end)
end end
return M return M

View File

@@ -193,25 +193,58 @@ function M.create_memo_in_buffer()
M.setup_buffer_for_editing() M.setup_buffer_for_editing()
end end
-- 【新增】创建居中浮动窗口的辅助函数
local function create_float_window(buf)
local width = math.floor(vim.o.columns * (config.window.width or 0.8))
local height = math.floor(vim.o.lines * (config.window.height or 0.8))
-- 计算居中位置
local row = math.floor((vim.o.lines - height) / 2)
local col = math.floor((vim.o.columns - width) / 2)
local opts = {
relative = "editor",
row = row,
col = col,
width = width,
height = height,
style = "minimal",
border = config.window.border or "rounded",
title = " Memos ",
title_pos = "center",
}
return vim.api.nvim_open_win(buf, true, opts)
end
function M.show_memos_list(filter) function M.show_memos_list(filter)
current_filter = filter current_filter = filter
local should_create_buf = true local should_create_buf = true
-- 检查 buffer 是否存在且有效
if buf_id and vim.api.nvim_buf_is_valid(buf_id) then if buf_id and vim.api.nvim_buf_is_valid(buf_id) then
should_create_buf = false should_create_buf = false
else else
buf_id = vim.api.nvim_create_buf(true, true) buf_id = vim.api.nvim_create_buf(false, true) -- 改为 false, true (unlisted, scratch)
vim.api.nvim_buf_set_name(buf_id, " Memos") vim.api.nvim_buf_set_name(buf_id, "MemosList")
vim.bo[buf_id].buftype = "nofile" vim.bo[buf_id].buftype = "nofile"
vim.bo[buf_id].swapfile = false vim.bo[buf_id].swapfile = false
vim.bo[buf_id].filetype = "memos_list" vim.bo[buf_id].filetype = "memos_list"
vim.bo[buf_id].modifiable = false vim.bo[buf_id].modifiable = false
end end
-- 检查该 buffer 是否已经在一个窗口中打开
local win_id = vim.fn.bufwinid(buf_id) local win_id = vim.fn.bufwinid(buf_id)
if win_id ~= -1 then if win_id ~= -1 then
vim.api.nvim_set_current_win(win_id) vim.api.nvim_set_current_win(win_id)
else else
vim.api.nvim_set_current_buf(buf_id) -- 【修改】根据配置决定打开方式
if config.window and config.window.enable_float then
create_float_window(buf_id)
else
-- 传统方式:直接切换到该 buffer
vim.api.nvim_set_current_buf(buf_id)
end
end end
vim.schedule(function() vim.schedule(function()