local M = {} -- Helper function to detect clipboard command local function detect_clipboard_cmd() -- Check for Wayland if vim.fn.executable("wl-copy") == 1 then return "wl-copy --type image/png" end -- Check for X11 if vim.fn.executable("xclip") == 1 then return "xclip -selection clipboard -t image/png" end -- Check for macOS if vim.fn.executable("pbcopy") == 1 then return "pbcopy" end -- Default fallback (Wayland) return "wl-copy --type image/png" end -- Configuration local config = { freeze_cmd = "freeze", clipboard_cmd = detect_clipboard_cmd(), } -- Helper function to detect visual selection local function get_visual_selection() local mode = vim.fn.mode() -- If we're in visual mode, capture directly from visual marks if mode:match("[vV]") then local v_pos = vim.fn.getpos("v") -- Start of visual selection local dot_pos = vim.fn.getpos(".") -- Current cursor position (end of selection) if v_pos[2] > 0 and dot_pos[2] > 0 then return math.min(v_pos[2], dot_pos[2]), math.max(v_pos[2], dot_pos[2]) end end return nil, nil end -- Helper function to get language from filetype local function get_language_from_filetype() local filetype = vim.bo.filetype if filetype == "" then return nil end return filetype end -- Helper function to validate yes/no input local function parse_yes_no(input, default) if not input or input == "" then return default end local lower = string.lower(input) if lower == "y" or lower == "yes" then return true elseif lower == "n" or lower == "no" then return false end return default end -- Helper function to build freeze command local function build_freeze_command(filepath, output, opts) local cmd = config.freeze_cmd .. " " .. vim.fn.shellescape(filepath) if opts.language then cmd = cmd .. " --language " .. vim.fn.shellescape(opts.language) end if opts.show_line_numbers then cmd = cmd .. " --show-line-numbers" end if opts.window then cmd = cmd .. " --window" end if opts.start_line and opts.end_line then cmd = cmd .. " --lines " .. opts.start_line .. "," .. opts.end_line end cmd = cmd .. " -o " .. vim.fn.shellescape(output) return cmd end -- Helper function to execute freeze and copy to clipboard local function execute_freeze(cmd, output) vim.fn.system(cmd) local clipboard_cmd = config.clipboard_cmd .. " < " .. vim.fn.shellescape(output) vim.fn.system(clipboard_cmd) vim.fn.delete(output) end local function freeze_screenshot(start_line, end_line) local filepath = vim.fn.expand("%:p") if filepath == "" then vim.notify("No file to screenshot", vim.log.levels.ERROR) return end -- Auto-detect visual selection if not provided if not start_line or not end_line then start_line, end_line = get_visual_selection() end local output = vim.fn.tempname() .. ".png" local cmd = config.freeze_cmd .. " " .. vim.fn.shellescape(filepath) .. " --show-line-numbers -o " .. vim.fn.shellescape(output) if start_line and end_line then cmd = cmd .. " --lines " .. start_line .. "," .. end_line end execute_freeze(cmd, output) local msg = start_line and string.format("Screenshot of lines %d–%d copied", start_line, end_line) or "Screenshot of entire file copied" vim.notify(msg, vim.log.levels.INFO) end local function freeze_interactive(start_line, end_line) local filepath = vim.fn.expand("%:p") if filepath == "" then vim.notify("No file to screenshot", vim.log.levels.ERROR) return end -- Check if snacks.nvim is available local ok, snacks = pcall(require, "snacks") if not ok or not snacks or not snacks.input then vim.notify("snacks.nvim is required for interactive mode", vim.log.levels.ERROR) return end -- Auto-detect visual selection if not provided if not start_line or not end_line then start_line, end_line = get_visual_selection() end local opts = { show_line_numbers = true, -- default: yes window = false, -- default: no language = get_language_from_filetype(), start_line = start_line, end_line = end_line, } -- First prompt: show line numbers snacks.input.input({ prompt = "Mostrar números de línea? (y/n)", default = "y", }, function(value) if value then opts.show_line_numbers = parse_yes_no(value, true) end -- Second prompt: show window controls snacks.input.input({ prompt = "Mostrar controles de ventana? (y/n)", default = "n", }, function(value2) if value2 then opts.window = parse_yes_no(value2, false) end -- Execute freeze with selected options local output = vim.fn.tempname() .. ".png" local cmd = build_freeze_command(filepath, output, opts) execute_freeze(cmd, output) local msg = start_line and string.format("Screenshot of lines %d–%d copied", start_line, end_line) or "Screenshot copied to clipboard" vim.notify(msg, vim.log.levels.INFO) end) end) end function M.setup(opts) opts = opts or {} -- Configure freeze command path if opts.freeze_cmd then config.freeze_cmd = opts.freeze_cmd end -- Configure clipboard command if opts.clipboard_cmd then config.clipboard_cmd = opts.clipboard_cmd end end -- Wrapper functions that properly handle visual mode -- These should be used in keymaps to ensure selection is captured correctly local function freeze_screenshot_wrapper() -- Try to get visual selection - this will work if called from visual mode keymap local start_line, end_line = get_visual_selection() freeze_screenshot(start_line, end_line) end local function freeze_interactive_wrapper() -- Try to get visual selection - this will work if called from visual mode keymap local start_line, end_line = get_visual_selection() freeze_interactive(start_line, end_line) end M.freeze_screenshot = freeze_screenshot M.freeze_interactive = freeze_interactive M.freeze_screenshot_wrapper = freeze_screenshot_wrapper M.freeze_interactive_wrapper = freeze_interactive_wrapper return M