-
Notifications
You must be signed in to change notification settings - Fork 53
Expand file tree
/
Copy pathimage_handler.lua
More file actions
161 lines (140 loc) · 4.92 KB
/
image_handler.lua
File metadata and controls
161 lines (140 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
--- Image pasting functionality from clipboard
--- @see https://github.com/sst/opencode/blob/45180104fe84e2d0b9d29be0f9f8a5e52d18e102/packages/opencode/src/cli/cmd/tui/util/clipboard.ts
local context = require('opencode.context')
local state = require('opencode.state')
local M = {}
--- Check if a file exists and has content
--- @param path string
--- @return boolean
local function is_valid_file(path)
return vim.fn.getfsize(path) > 0
end
--- Run shell or powershell command and return success
--- @param cmd string|table
--- @param opts table?
--- @return boolean
local function run_shell_cmd(cmd, opts)
local sys_cmd
if type(cmd) == 'string' then
sys_cmd = { 'sh', '-c', cmd }
else
sys_cmd = cmd
end
return vim.system(sys_cmd, opts):wait().code == 0
end
--- Save base64 data to file
--- @param data string
--- @param path string
--- @return boolean
local function save_base64(data, path)
if vim.fn.has('win32') == 1 then
local script =
string.format('[System.IO.File]::WriteAllBytes("%s", [System.Convert]::FromBase64String("%s"))', path, data)
return run_shell_cmd({ 'powershell.exe', '-command', '-' }, { stdin = script })
else
local decode_arg = vim.uv.os_uname().sysname == 'Darwin' and '-D' or '-d'
return run_shell_cmd(string.format('base64 %s > "%s"', decode_arg, path), { stdin = data })
end
end
--- macOS clipboard image handler using osascript
--- @param path string
--- @return boolean
local function handle_darwin_clipboard(path)
if vim.fn.executable('osascript') ~= 1 then
return false
end
local cmd = string.format(
"osascript -e 'set imageData to the clipboard as \"PNGf\"' -e 'set fileRef to open for access POSIX file \"%s\" with write permission' -e 'set eof fileRef to 0' -e 'write imageData to fileRef' -e 'close access fileRef'",
path
)
return run_shell_cmd(cmd)
end
--- Linux clipboard image handler supporting Wayland and X11
--- @param path string
--- @return boolean
local function handle_linux_clipboard(path)
if vim.fn.executable('wl-paste') == 1 and run_shell_cmd(string.format('wl-paste -t image/png > "%s"', path)) then
return true
end
return vim.fn.executable('xclip') == 1
and run_shell_cmd(string.format('xclip -selection clipboard -t image/png -o > "%s"', path))
end
--- Windows clipboard image handler using PowerShell
--- @param path string
--- @return boolean
local function handle_windows_clipboard(path)
if vim.fn.executable('powershell.exe') ~= 1 then
return false
end
local script = string.format(
[[
Add-Type -AssemblyName System.Windows.Forms;
$img = [System.Windows.Forms.Clipboard]::GetImage();
if ($img) {
$img.Save('%s', [System.Drawing.Imaging.ImageFormat]::Png);
} else {
exit 1
}
]],
path
)
return run_shell_cmd({ 'powershell.exe', '-command', '-' }, { stdin = script })
end
local handlers = {
Darwin = handle_darwin_clipboard,
Linux = handle_linux_clipboard,
Windows_NT = handle_windows_clipboard,
}
--- Try to get image from system clipboard
--- @param image_path string
--- @return boolean
local function try_system_clipboard(image_path)
local os_name = vim.uv.os_uname().sysname
local handler = handlers[os_name]
-- WSL detection and override
if vim.fn.exists('$WSL_DISTRO_NAME') == 1 then
handler = handlers.Windows_NT
end
return handler and handler(image_path) and is_valid_file(image_path) or false
end
--- Try to parse base64 image data from clipboard
--- @param temp_dir string
--- @param timestamp string
--- @return boolean, string?
local function try_base64_clipboard(temp_dir, timestamp)
local content = vim.fn.getreg('+')
if not content or content == '' then
return false
end
local format, data = content:match('^data:image/([^;]+);base64,(.+)$')
if not format or not data then
return false
end
local image_path = string.format('%s/pasted_image_%s.%s', temp_dir, timestamp, format)
local success = save_base64(data, image_path) and is_valid_file(image_path)
return success, success and image_path or nil
end
--- Handle clipboard image data by saving it to a file and adding it to context
--- @return boolean success True if image was successfully handled
function M.paste_image_from_clipboard()
local temp_dir = vim.fn.tempname()
vim.fn.mkdir(temp_dir, 'p')
local timestamp = os.date('%Y%m%d_%H%M%S')
local image_path = string.format('%s/pasted_image_%s.png', temp_dir, timestamp)
local success = try_system_clipboard(image_path)
if not success then
local base64_success, base64_path = try_base64_clipboard(temp_dir, timestamp --[[@as string]])
success = base64_success
if base64_path then
image_path = base64_path
end
end
if success then
context.add_file(image_path)
vim.notify('Image saved and added to context: ' .. vim.fn.fnamemodify(image_path, ':t'), vim.log.levels.INFO)
return true
end
vim.notify('No image found in clipboard.', vim.log.levels.WARN)
return false
end
return M