From ba77cb6aa9e4acf6ce2e1ba1d5f346fd67235c31 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Wed, 26 Nov 2025 13:28:20 +0000 Subject: [PATCH] Use OS native copy. --- go.mod | 4 --- go.sum | 8 ----- internal/ui/wizard_handlers.go | 60 ++++++++++++++++++++++++++++------ 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 0fa905a..d274eda 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/go-logr/logr v1.4.3 github.com/grandcat/zeroconf v1.0.0 github.com/stretchr/testify v1.11.1 - golang.design/x/clipboard v0.7.1 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.34.2 k8s.io/apimachinery v0.34.2 @@ -71,9 +70,6 @@ require ( github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/exp/shiny v0.0.0-20251125195548-87e1e737ad39 // indirect - golang.org/x/image v0.33.0 // indirect - golang.org/x/mobile v0.0.0-20251113184115-a159579294ab // indirect golang.org/x/mod v0.30.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.33.0 // indirect diff --git a/go.sum b/go.sum index 326c9e1..63073f8 100644 --- a/go.sum +++ b/go.sum @@ -158,19 +158,11 @@ go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.design/x/clipboard v0.7.1 h1:OEG3CmcYRBNnRwpDp7+uWLiZi3hrMRJpE9JkkkYtz2c= -golang.design/x/clipboard v0.7.1/go.mod h1:i5SiIqj0wLFw9P/1D7vfILFK0KHMk7ydE72HRrUIgkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/exp/shiny v0.0.0-20251125195548-87e1e737ad39 h1:fy+QQHOvRvUJ5ZJigptKDpFN332kInaZSFvlb0CrwGA= -golang.org/x/exp/shiny v0.0.0-20251125195548-87e1e737ad39/go.mod h1:p7Wr/QhhC3SjhTsG6HN+87un+wDRHZIBEPvfbo51ToQ= -golang.org/x/image v0.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= -golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= -golang.org/x/mobile v0.0.0-20251113184115-a159579294ab h1:Iqyc+2zr7aGyLuEadIm0KRJP0Wwt+fhlXLa51Fxf1+Q= -golang.org/x/mobile v0.0.0-20251113184115-a159579294ab/go.mod h1:Eq3Nh/5pFSWug2ohiudJ1iyU59SO78QFuh4qTTN++I0= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= diff --git a/internal/ui/wizard_handlers.go b/internal/ui/wizard_handlers.go index 898625b..7ff8fb4 100644 --- a/internal/ui/wizard_handlers.go +++ b/internal/ui/wizard_handlers.go @@ -3,6 +3,8 @@ package ui import ( "context" "fmt" + "os/exec" + "runtime" "strconv" "strings" "time" @@ -10,7 +12,6 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/nvm/kportal/internal/config" "github.com/nvm/kportal/internal/k8s" - "golang.design/x/clipboard" ) // isFilterableStep returns true if the step supports search/filter @@ -1186,19 +1187,15 @@ func (m model) handleHTTPLogKeys(msg tea.KeyMsg) (tea.Model, tea.Cmd) { if entry.ResponseBody != "" { // Decompress if needed before copying body := decompressContent(entry.ResponseBody, entry.ResponseHeaders) - if err := clipboard.Init(); err == nil { - clipboard.Write(clipboard.FmtText, []byte(body)) + if err := copyToClipboard(body); err == nil { state.copyMessage = "Copied!" - // Clear the message after 2 seconds - return m, tea.Tick(2*time.Second, func(t time.Time) tea.Msg { - return clearCopyMessageMsg{} - }) } else { state.copyMessage = "Clipboard unavailable" - return m, tea.Tick(2*time.Second, func(t time.Time) tea.Msg { - return clearCopyMessageMsg{} - }) } + // Clear the message after 2 seconds + return m, tea.Tick(2*time.Second, func(t time.Time) tea.Msg { + return clearCopyMessageMsg{} + }) } } return m, nil @@ -1420,3 +1417,46 @@ func (m model) handleBenchmarkComplete(msg BenchmarkCompleteMsg) (tea.Model, tea return m, nil } + +// copyToClipboard copies text to the system clipboard using OS-specific commands. +// This avoids CGO dependencies that cause issues in CI environments. +func copyToClipboard(text string) error { + var cmd *exec.Cmd + + switch runtime.GOOS { + case "darwin": + cmd = exec.Command("pbcopy") + case "linux": + // Try xclip first, fall back to xsel + if _, err := exec.LookPath("xclip"); err == nil { + cmd = exec.Command("xclip", "-selection", "clipboard") + } else if _, err := exec.LookPath("xsel"); err == nil { + cmd = exec.Command("xsel", "--clipboard", "--input") + } else { + return fmt.Errorf("no clipboard tool found (install xclip or xsel)") + } + case "windows": + cmd = exec.Command("clip") + default: + return fmt.Errorf("unsupported platform: %s", runtime.GOOS) + } + + stdin, err := cmd.StdinPipe() + if err != nil { + return err + } + + if err := cmd.Start(); err != nil { + return err + } + + if _, err := stdin.Write([]byte(text)); err != nil { + return err + } + + if err := stdin.Close(); err != nil { + return err + } + + return cmd.Wait() +}