This commit is contained in:
2026-06-23 05:02:15 +08:00
commit e6f1776d4f
264 changed files with 54215 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
package tui
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
tea "charm.land/bubbletea/v2"
)
// errNoClipboardImage means the system clipboard holds no image data.
var errNoClipboardImage = errors.New("剪贴板没有图片")
// clipboardRunner is the injectable command runner (replaced in tests).
var clipboardRunner = func(name string, args ...string) ([]byte, error) {
return exec.Command(name, args...).Output()
}
// imageMediaExt maps clipboard image MIME types to file extensions, in
// preference order.
var imageMediaExt = []struct{ mime, ext string }{
{"image/png", "png"},
{"image/jpeg", "jpg"},
{"image/jpg", "jpg"},
{"image/gif", "gif"},
{"image/webp", "webp"},
}
func pickImageMime(types string) (mime, ext string) {
for _, m := range imageMediaExt {
if strings.Contains(types, m.mime) {
return m.mime, m.ext
}
}
return "", ""
}
// readClipboardImage reads an image off the system clipboard via the available
// helper (wl-paste on Wayland, xclip on X11). Returns errNoClipboardImage when
// the clipboard holds no image.
func readClipboardImage() (data []byte, ext string, err error) {
if _, e := exec.LookPath("wl-paste"); e == nil {
types, _ := clipboardRunner("wl-paste", "--list-types")
mime, ext := pickImageMime(string(types))
if mime == "" {
return nil, "", errNoClipboardImage
}
data, err := clipboardRunner("wl-paste", "--type", mime)
if err != nil {
return nil, "", fmt.Errorf("wl-paste: %w", err)
}
return data, ext, nil
}
if _, e := exec.LookPath("xclip"); e == nil {
targets, _ := clipboardRunner("xclip", "-selection", "clipboard", "-t", "TARGETS", "-o")
mime, ext := pickImageMime(string(targets))
if mime == "" {
return nil, "", errNoClipboardImage
}
data, err := clipboardRunner("xclip", "-selection", "clipboard", "-t", mime, "-o")
if err != nil {
return nil, "", fmt.Errorf("xclip: %w", err)
}
return data, ext, nil
}
return nil, "", errors.New("未找到剪贴板工具 (wl-paste/xclip)")
}
// pasteImageCmd reads a clipboard image and writes it to the inbox as
// <index>.<ext>, mirroring the VSCode spx-inbox scheme.
func pasteImageCmd(inboxDir string, index int) tea.Cmd {
return func() tea.Msg {
data, ext, err := readClipboardImage()
if err != nil {
return imagePastedMsg{err: err}
}
if err := os.MkdirAll(inboxDir, 0o755); err != nil {
return imagePastedMsg{err: fmt.Errorf("创建 inbox: %w", err)}
}
path := filepath.Join(inboxDir, fmt.Sprintf("%d.%s", index, ext))
if err := os.WriteFile(path, data, 0o644); err != nil {
return imagePastedMsg{err: fmt.Errorf("写入图片: %w", err)}
}
return imagePastedMsg{path: path, index: index}
}
}