charmbracelet / charmbracelet/bubbles

textinput v1/v2: Wayland Pasting Can Sometimes Cause Unexpected Errors

Open
#960 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
Go
Stars
8.9k
Forks
457
Avg merge
1d 18h
Merged PRs (30d)
5

Description

**Describe the bug**
When on Wayland, there is an issue where when you copy from say VSCode and then close VSCode and then try to paste into the textinput component for bubbletea, it just causes an error that says `exit status 1`. There is really no good context to go off of since the underlying paste err type is not exported.

**Setup**
Please complete the following information along with version numbers, if applicable.
- OS Pop OS 24.04 (really any Wayland Linux OS will do the trick, this likely is Wayland specific)
- Shell bash
- Terminal Emulator Kitty and Ghostty
- Terminal Multiplexer with and without Tmux
- Locale en_US.UTF-8

**To Reproduce**

Setup the following main.go:
``` Go
package main

import (
"log"
"strings"

"charm.land/bubbles/v2/textinput"
tea "charm.land/bubbletea/v2"
"github.com/atotto/clipboard"
)

func main() {
ti := textinput.New()
ti.SetWidth(20)
ti.CharLimit = 200
ti.Placeholder = "Section break"
ti.Focus()

var model = simpleModel{
input: ti,
}

p := tea.NewProgram(&model)
finalModel, err := p.Run()
if err != nil {
log.Fatal(err)
}

model, _ = finalModel.(simpleModel)
if model.Err != nil {
log.Fatalf("Something went wrong while running the TUI: %s\n", model.Err)
}
}

type simpleModel struct {
ready bool
input textinput.Model
pasted string
Err error
}

func (m simpleModel) Init() tea.Cmd {
return nil
}

func (m simpleModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.Err != nil {
return m, tea.Quit
}

// general logic for handling keys here
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c":

return m, tea.Quit
case "ctrl+v":
pasted, err := clipboard.ReadAll()
if err == nil { // we are just using the err here to see if we got something from the clipboard to set the pasted value...
m.pasted = pasted
}
}
case tea.WindowSizeMsg:
m.ready = true
case error:
m.Err = msg

return m, tea.Quit
}

// we will do update last to make sure we can try to capture paste events before things crash, but it doesn't really matter if this is first
var cmd tea.Cmd
m.input, cmd = m.input.Update(msg)

return m, cmd
}

func (m simpleModel) View() tea.View {
v := tea.NewView("")
v.AltScreen = true
if m.ready {
var content strings.Builder
if m.pasted != "" {
content.WriteString("Last pasted string: ")
content.WriteString(m.pasted)
content.WriteString("\n")
}

content.WriteString(m.input.View())

v.SetContent(content.String())
}

return v
}
```

Then run it via `go run main.go`. Once that is done you can open a file in VSCode or any other GUI application that you can copy something from.

Copy something from the file you just opened and then close the GUI you copied it from.

Now go back to the TUI and try to paste. It should cause the program to crash due to an unexpected error of `exist status 1`.

**Source Code**
See code above.

**Expected behavior**
I am not really sure an empty clipboard should result in an error on paste, but that is not really something you have control over as there is an external package used. It would be helpful to expose a specific error type or event a specific error that can then be caught with `errors.Is` to allow for handling easily outside of the text input bubble.

**Screenshots**
Add screenshots to help explain your problem.

**Additional context**
Sample image of what it looks like when the program fails:

Image

So here is what the problem seems to be:
- Wayland has a security rule where if you close a program, any content copied from it gets removed from your clipboard
- Wayland uses `wl-paste` which throws an error whenever the clipboard is empty as can be seen in the next image

Image

- The clipboard package also uses `wl-paste` when it is installed: https://github.com/atotto/clipboard/blob/8ac5904afc29480e1ed50ebb8c6e9a28a48a0506/clipboard_unix.go#L52-L61
- But the output as shown prior is going to be `exit status 1` when running the exec command and checking for an error [here](https://github.com/atotto/clipboard/blob/8ac5904afc29480e1ed50ebb8c6e9a28a48a0506/clipboard_unix.go#L113-L127)
- Thus when text input uses the clipboard [here](https://github.com/charmbracelet/bubbles/blob/42130e89579382bb1944b4b1d57e75cae5286d01/textinput/textinput.go#L789), it will throw an error when the clipboard is empty
- Any paste error messages will not be identifiable as a paste error due to the error not being exported: https://github.com/charmbracelet/bubbles/blob/42130e89579382bb1944b4b1d57e75cae5286d01/textinput/textinput.go#L24

I hope this helps. Thanks for all the work that y'all have done!

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.