Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/build-artifacts-and-draft-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ jobs:
- uses: actions/setup-go@v4
with:
go-version: 1.24
- name: Install x11
run: sudo apt-get install -qqy libx11-dev
- name: Build
uses: goreleaser/goreleaser-action@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:

- name: Install zsh
# This is used for the TestDefaultShell test
run: sudo apt-get install -qqy zsh libx11-dev just
run: sudo apt-get install -qqy zsh just

- name: Generate and format code
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
- name: Install tools
run: |
sudo apt-get update
sudo apt-get install -qqy vim htop libx11-dev just
sudo apt-get install -qqy vim htop just

- name: Build Docusaurus site
run: just docs-build-assets
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Native Wayland clipboard support (via `golang.design/x/clipboard` v0.8.0); building on Linux no longer needs `libx11-dev`

## 1.12.0 - 2026-03-27

- Layout `pane` node renamed to `view`
Expand Down
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ just api

# Generate sqlc code
just sqlc

# Regenerate vendor/ and re-apply local patches to vendored modules
# (never run `go mod vendor` directly; see patches/README.md)
just vendor
```

### Running Individual Tests
Expand All @@ -67,6 +71,10 @@ go test ./pkg/mux/...
go test -v ./pkg/cy -run TestAPI
```

### Vendored dependencies

`vendor/` is checked in and a few vendored modules carry local patches (listed in `patches/README.md`). Running `go mod vendor` directly discards those patches. Use `just vendor` instead, which re-applies them, and regenerate the affected patch file when bumping a patched module.

### Building

The main executable is `cmd/cy/main.go`. Build output goes to `./cy` in the repository root.
Expand Down
10 changes: 6 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ require (
github.com/stretchr/testify v1.10.0
github.com/ugorji/go/codec v1.2.11
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e
golang.design/x/clipboard v0.7.0
golang.org/x/image v0.14.0
golang.design/x/clipboard v0.8.0
golang.org/x/image v0.28.0
golang.org/x/sync v0.16.0
golang.org/x/term v0.33.0
nhooyr.io/websocket v1.8.7
Expand All @@ -51,6 +51,7 @@ require (
github.com/dave/jennifer v1.7.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/ebitengine/purego v0.10.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
Expand Down Expand Up @@ -92,10 +93,11 @@ require (
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.design/x/x11 v0.2.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/exp/shiny v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a // indirect
golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 // indirect
golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f // indirect
golang.org/x/mod v0.26.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sys v0.34.0 // indirect
Expand Down
20 changes: 12 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
Expand Down Expand Up @@ -245,18 +247,20 @@ go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.design/x/clipboard v0.7.0 h1:4Je8M/ys9AJumVnl8m+rZnIvstSnYj1fvzqYrU3TXvo=
golang.design/x/clipboard v0.7.0/go.mod h1:PQIvqYO9GP29yINEfsEn5zSQKAz3UgXmZKzDA6dnq2E=
golang.design/x/clipboard v0.8.0 h1:6VEcH28wwcSgKc+vnxHHDWiRjrakTQIAJnPUrt3aOgg=
golang.design/x/clipboard v0.8.0/go.mod h1:s0pwrtA3Q9fgnVtGDmP5ZK/pp55cQKB23esKsjwWhWM=
golang.design/x/x11 v0.2.0 h1:Uiwu2guGihsJX/ZCzpoDPFz5gR/Qntm08mvoBCmRydo=
golang.design/x/x11 v0.2.0/go.mod h1:/5q1mFkdc1rL8mvB7DsQFi6as4tIkBv4FXjcP07mrkE=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/exp/shiny v0.0.0-20240909161429-701f63a606c0 h1:J0D+bkvD51EjdYYhLL7Y+iLhJZ+ejs93NGVnH1W/jUE=
golang.org/x/exp/shiny v0.0.0-20240909161429-701f63a606c0/go.mod h1:3F+MieQB7dRYLTmnncoFbb1crS5lfQoTfDgQy6K4N0o=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a h1:sYbmY3FwUWCBTodZL1S3JUuOvaW6kM2o+clDzzDNBWg=
golang.org/x/mobile v0.0.0-20231127183840-76ac6878050a/go.mod h1:Ede7gF0KGoHlj822RtphAHK1jLdrcuRBZg0sF1Q+SPc=
golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 h1:Wdx0vgH5Wgsw+lF//LJKmWOJBLWX6nprsMqnf99rYDE=
golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8=
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f h1:/n+PL2HlfqeSiDCuhdBbRNlGS/g2fM4OHufalHaTVG8=
golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f/go.mod h1:ESkJ836Z6LpG6mTVAhA48LpfW/8fNR0ifStlH2axyfg=
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
Expand Down
8 changes: 8 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,11 @@ ci:
just lint
just build
just test-race

# Regenerate the vendor directory and re-apply cy's local patches to
# vendored modules. Always use this instead of `go mod vendor` directly,
# since `go mod vendor` overwrites the patched files. See patches/README.md.
vendor:
go mod tidy
go mod vendor
git apply patches/*.patch
27 changes: 27 additions & 0 deletions patches/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Vendor patches

cy carries a few small local modifications to vendored Go modules. `go mod
vendor` overwrites the vendored sources with pristine upstream copies, so the
modifications live here as patches (relative to the repository root) and are
re-applied by `just vendor`.

| Patch | Module | What it does |
| --- | --- | --- |
| `golang.design-x-clipboard.patch` | `golang.design/x/clipboard` | Adds `ReadErr` and makes `Write` return an `error` so clipboard failures reach the user instead of being swallowed. |
| `github.com-danielgatis-go-vte.patch` | `github.com/danielgatis/go-vte` | Colon (`:`) sub-parameter support in CSI sequences, used for styled underlines. |
| `github.com-sevlyar-go-daemon.patch` | `github.com/sevlyar/go-daemon` | Drops the string wrapping of lock-file errors so callers can inspect the underlying error. |

## Updating a patched module

1. Bump the version in `go.mod`.
2. Run `just vendor`. If a patch no longer applies cleanly, `git apply` will
say so; fix up the vendored file by hand, then regenerate the patch:

```bash
diff -u --label a/vendor/<mod>/<file> --label b/vendor/<mod>/<file> \
"$(go env GOMODCACHE)/<mod>@<version>/<file>" vendor/<mod>/<file> \
> patches/<name>.patch
```

3. Run `just vendor` again and confirm `git status` shows no unexpected
changes under `vendor/`.
52 changes: 52 additions & 0 deletions patches/github.com-danielgatis-go-vte.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--- a/vendor/github.com/danielgatis/go-vte/vtparser/parser.go
+++ b/vendor/github.com/danielgatis/go-vte/vtparser/parser.go
@@ -14,7 +14,7 @@

const maxIntermediates = 2
const maxOscRaw = 1024
-const maxParams = 16
+const maxParams = 32

type printCallback func(char rune)
type execCallback func(b byte)
@@ -282,10 +282,19 @@
return
}

- if b == ';' {
+ if b == ';' || b == ':' {
p.params[idx] = p.param
p.param = 0
p.numParams++
+ if b == ':' {
+ // Insert sentinel to distinguish colon
+ // sub-parameters from semicolons
+ idx = p.numParams
+ if idx < maxParams {
+ p.params[idx] = -1
+ p.numParams++
+ }
+ }
} else {
p.param = smul64(p.param, 10)
p.param = sadd64(p.param, int64((b - '0')))
--- a/vendor/github.com/danielgatis/go-vte/vtparser/table.go
+++ b/vendor/github.com/danielgatis/go-vte/vtparser/table.go
@@ -376,7 +376,7 @@
csiParamState | (paramAction << 4), // 37
csiParamState | (paramAction << 4), // 38
csiParamState | (paramAction << 4), // 39
- csiIgnoreState | (noneAction << 4), // 3A
+ csiParamState | (paramAction << 4), // 3A
csiParamState | (paramAction << 4), // 3B
csiParamState | (collectAction << 4), // 3C
csiParamState | (collectAction << 4), // 3D
@@ -1150,7 +1150,7 @@
anywhereState | (paramAction << 4), // 37
anywhereState | (paramAction << 4), // 38
anywhereState | (paramAction << 4), // 39
- csiIgnoreState | (noneAction << 4), // 3A
+ anywhereState | (paramAction << 4), // 3A
anywhereState | (paramAction << 4), // 3B
csiIgnoreState | (noneAction << 4), // 3C
csiIgnoreState | (noneAction << 4), // 3D
14 changes: 14 additions & 0 deletions patches/github.com-sevlyar-go-daemon.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--- a/vendor/github.com/sevlyar/go-daemon/daemon_unix.go
+++ b/vendor/github.com/sevlyar/go-daemon/daemon_unix.go
@@ -134,9 +134,11 @@
return err
}
if d.pidFile, err = OpenLockFile(d.PidFileName, d.PidFilePerm); err != nil {
+ err = fmt.Errorf("OpenLockFile: %s", err.Error())
return
}
if err = d.pidFile.Lock(); err != nil {
+ err = fmt.Errorf("Lock: %s %s", d.PidFileName, err.Error())
return
}
if len(d.Chroot) > 0 {
54 changes: 54 additions & 0 deletions patches/golang.design-x-clipboard.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
--- a/vendor/golang.design/x/clipboard/clipboard.go
+++ b/vendor/golang.design/x/clipboard/clipboard.go
@@ -178,14 +178,20 @@
return buf
}

+// ReadErr is like Read, but reports the underlying error instead of
+// swallowing it. (cy patch)
+func ReadErr(t Format) ([]byte, error) {
+ lock.Lock()
+ defer lock.Unlock()
+ return read(t)
+}
+
// Write writes a given buffer to the clipboard in a specified format.
//
-// The data is on the clipboard as soon as Write returns; consuming the
-// returned channel is optional. That channel receives a single empty
-// struct, and is then closed, only when the clipboard is later overwritten
-// by another writer (detected via the platform clipboard sequence number).
-// If nothing else ever overwrites the clipboard, the channel never fires —
-// so do not block on it expecting it to report that this write completed.
+// The data is on the clipboard as soon as Write returns. Upstream returns
+// a channel that fires when the clipboard is later overwritten; cy patches
+// this to return the write error instead, since it never used the
+// channel and needs to surface failures to the user. (cy patch)
//
// If format t indicates an image, buf is normalized to PNG before being placed
// on the clipboard. PNG input is stored as-is; other formats are accepted if the
@@ -193,21 +199,15 @@
// _ "image/jpeg" or _ "golang.org/x/image/webp"), and undecodable input passes
// through unchanged. The clipboard therefore always serves PNG, regardless of
// the input encoding.
-func Write(t Format, buf []byte) <-chan struct{} {
+func Write(t Format, buf []byte) error {
lock.Lock()
defer lock.Unlock()

if t == FmtImage {
buf = toPNG(buf)
}
- changed, err := write(t, buf)
- if err != nil {
- if debug {
- fmt.Fprintf(os.Stderr, "write to clipboard err: %v\n", err)
- }
- return nil
- }
- return changed
+ _, err := write(t, buf)
+ return err
}

// toPNG normalizes an FmtImage payload to canonical PNG: the clipboard stores
1 change: 1 addition & 0 deletions vendor/github.com/ebitengine/purego/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading