feat: native clipboard image sync (Linux, opt-in sync_images)

This commit is contained in:
2026-08-14 09:51:25 -04:00
parent 884415c1c1
commit 147cba6d2a
15 changed files with 274 additions and 48 deletions
+41 -8
View File
@@ -4,7 +4,9 @@ package server
import (
"crypto/subtle"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
"sync"
@@ -119,8 +121,9 @@ func (s *Server) handleClip(w http.ResponseWriter, r *http.Request) {
return
}
if p.Text == "" {
http.Error(w, "bad request: empty text", http.StatusBadRequest)
isImage := p.Data != ""
if p.Text == "" && !isImage {
http.Error(w, "bad request: empty content", http.StatusBadRequest)
return
}
@@ -137,13 +140,21 @@ func (s *Server) handleClip(w http.ResponseWriter, r *http.Request) {
return
}
if err := s.clipboard.Write(p.Text); err != nil {
log.Printf("server: clipboard write: %v", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
if isImage {
if err := s.writeImage(p); err != nil {
log.Printf("server: image write: %v", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
} else {
if err := s.clipboard.Write(p.Text); err != nil {
log.Printf("server: clipboard write: %v", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
}
s.filter.MarkWritten(p.Text)
s.filter.MarkWrittenPayload(p)
s.recordReceive(p)
if s.opts.History != nil {
@@ -151,7 +162,11 @@ func (s *Server) handleClip(w http.ResponseWriter, r *http.Request) {
}
if s.opts.Notifier != nil {
if err := s.opts.Notifier.Notify("clip-sync", "Clip reçu de "+p.Origin); err != nil {
msg := "Clip reçu de " + p.Origin
if isImage {
msg = "Image reçue de " + p.Origin
}
if err := s.opts.Notifier.Notify("clip-sync", msg); err != nil {
log.Printf("server: notify: %v", err)
}
}
@@ -159,6 +174,24 @@ func (s *Server) handleClip(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
}
// writeImage decodes and writes an image payload to the clipboard. It requires
// the clipboard implementation to support ImageClipboard.
func (s *Server) writeImage(p dedup.Payload) error {
ic, ok := s.clipboard.(clipboard.ImageClipboard)
if !ok {
return fmt.Errorf("image clipboard not supported on this platform")
}
data, err := base64.StdEncoding.DecodeString(p.Data)
if err != nil {
return fmt.Errorf("decode image: %w", err)
}
mime := p.Mime
if mime == "" {
mime = "image/png"
}
return ic.WriteImage(mime, data)
}
func (s *Server) recordReceive(p dedup.Payload) {
s.mu.Lock()
defer s.mu.Unlock()