My nvim configuration
feat(claude): prompt stays available on escape
1 file changed, +47 -16
+47-16claude-nvim/lua/claude/ui.lua
| @@ -364,6 +364,20 @@ function M.resume_session(claude_id) | ||
| 364 | 364 | append_lines({ "Press `i` to send a prompt.", "" }) |
| 365 | 365 | end |
| 366 | 366 | |
| 367 | +local prompt_buf = nil | |
| 368 | +local prompt_keymaps_set = false | |
| 369 | + | |
| 370 | +local function ensure_prompt_buf() | |
| 371 | + if prompt_buf and vim.api.nvim_buf_is_valid(prompt_buf) then | |
| 372 | + return prompt_buf | |
| 373 | + end | |
| 374 | + prompt_buf = vim.api.nvim_create_buf(false, true) | |
| 375 | + vim.bo[prompt_buf].bufhidden = "hide" | |
| 376 | + vim.bo[prompt_buf].filetype = "markdown" | |
| 377 | + prompt_keymaps_set = false | |
| 378 | + return prompt_buf | |
| 379 | +end | |
| 380 | + | |
| 367 | 381 | function M.open_input(opts) |
| 368 | 382 | opts = opts or {} |
| 369 | 383 | if not panel.sid then |
| @@ -372,9 +386,7 @@ function M.open_input(opts) | ||
| 372 | 386 | local sid = panel.sid |
| 373 | 387 | local return_to = opts.return_to |
| 374 | 388 | local context = opts.context |
| 375 | - local input_buf = vim.api.nvim_create_buf(false, true) | |
| 376 | - vim.bo[input_buf].bufhidden = "wipe" | |
| 377 | - vim.bo[input_buf].filetype = "markdown" | |
| 389 | + local input_buf = ensure_prompt_buf() | |
| 378 | 390 | |
| 379 | 391 | local width = math.min(80, math.max(40, math.floor(vim.o.columns * 0.6))) |
| 380 | 392 | local height = 8 |
| @@ -390,15 +402,19 @@ function M.open_input(opts) | ||
| 390 | 402 | or " Claude prompt — <C-s> send <C-c> cancel ", |
| 391 | 403 | title_pos = "center", |
| 392 | 404 | }) |
| 393 | - vim.cmd("startinsert") | |
| 405 | + -- Place cursor at end of existing text so user can keep typing | |
| 406 | + local lc = vim.api.nvim_buf_line_count(input_buf) | |
| 407 | + local last = vim.api.nvim_buf_get_lines(input_buf, lc - 1, lc, false)[1] or "" | |
| 408 | + pcall(vim.api.nvim_win_set_cursor, win, { lc, #last }) | |
| 409 | + vim.cmd("startinsert!") | |
| 394 | 410 | |
| 395 | 411 | local closed = false |
| 396 | - local function close() | |
| 412 | + local function close_keep() | |
| 397 | 413 | if closed then return end |
| 398 | 414 | closed = true |
| 399 | 415 | pcall(vim.cmd, "stopinsert") |
| 400 | 416 | if vim.api.nvim_win_is_valid(win) then |
| 401 | - vim.api.nvim_win_close(win, true) | |
| 417 | + vim.api.nvim_win_close(win, false) | |
| 402 | 418 | end |
| 403 | 419 | if return_to and vim.api.nvim_win_is_valid(return_to) then |
| 404 | 420 | pcall(vim.api.nvim_set_current_win, return_to) |
| @@ -408,23 +424,38 @@ function M.open_input(opts) | ||
| 408 | 424 | if closed then return end |
| 409 | 425 | local lines = vim.api.nvim_buf_get_lines(input_buf, 0, -1, false) |
| 410 | 426 | local text = table.concat(lines, "\n") |
| 411 | - if text:match("^%s*$") then close(); return end | |
| 427 | + if text:match("^%s*$") then close_keep(); return end | |
| 428 | + -- clear the persistent prompt buffer since prompt was sent | |
| 429 | + if vim.api.nvim_buf_is_valid(input_buf) then | |
| 430 | + vim.api.nvim_buf_set_lines(input_buf, 0, -1, false, {}) | |
| 431 | + end | |
| 432 | + local final_text = text | |
| 412 | 433 | if context and context ~= "" then |
| 413 | - text = context .. "\n\n" .. text | |
| 434 | + final_text = context .. "\n\n" .. text | |
| 414 | 435 | end |
| 415 | - M.append_user_prompt(sid, text) | |
| 416 | - close() | |
| 436 | + M.append_user_prompt(sid, final_text) | |
| 437 | + close_keep() | |
| 417 | 438 | local runner = require("claude.runner") |
| 418 | - local ok, err = runner.send(sid, text, { skip_ui_prompt = true }) | |
| 439 | + local ok, err = runner.send(sid, final_text, { skip_ui_prompt = true }) | |
| 419 | 440 | if not ok then |
| 420 | 441 | vim.notify("claude: " .. tostring(err), vim.log.levels.WARN) |
| 421 | 442 | end |
| 422 | 443 | end |
| 423 | - local km = { buffer = input_buf, nowait = true, silent = true } | |
| 424 | - vim.keymap.set({ "n", "i" }, "<C-s>", send, km) | |
| 425 | - vim.keymap.set({ "n", "i" }, "<C-c>", close, km) | |
| 426 | - vim.keymap.set("n", "q", close, km) | |
| 427 | - vim.keymap.set("n", "<Esc>", close, km) | |
| 444 | + if not prompt_keymaps_set then | |
| 445 | + local km = { buffer = input_buf, nowait = true, silent = true } | |
| 446 | + vim.keymap.set({ "n", "i" }, "<C-s>", send, km) | |
| 447 | + vim.keymap.set({ "n", "i" }, "<C-c>", close_keep, km) | |
| 448 | + vim.keymap.set("n", "q", close_keep, km) | |
| 449 | + vim.keymap.set("n", "<Esc>", close_keep, km) | |
| 450 | + prompt_keymaps_set = true | |
| 451 | + else | |
| 452 | + -- rebind send/close so they reference current win/sid/context closures | |
| 453 | + local km = { buffer = input_buf, nowait = true, silent = true } | |
| 454 | + vim.keymap.set({ "n", "i" }, "<C-s>", send, km) | |
| 455 | + vim.keymap.set({ "n", "i" }, "<C-c>", close_keep, km) | |
| 456 | + vim.keymap.set("n", "q", close_keep, km) | |
| 457 | + vim.keymap.set("n", "<Esc>", close_keep, km) | |
| 458 | + end | |
| 428 | 459 | end |
| 429 | 460 | |
| 430 | 461 | local function capture_visual_selection(bufnr) |