Browse Source

chore: internal clipboard package

adamdottv 7 months ago
parent
commit
3f25e5bf86
40 changed files with 8 additions and 1627 deletions
  1. 0 12
      packages/tui/clipboard/.github/FUNDING.yml
  2. 0 71
      packages/tui/clipboard/.github/workflows/clipboard.yml
  3. 0 15
      packages/tui/clipboard/.gitignore
  4. 0 13
      packages/tui/clipboard/Dockerfile
  5. 0 21
      packages/tui/clipboard/LICENSE
  6. 0 162
      packages/tui/clipboard/README.md
  7. 0 80
      packages/tui/clipboard/clipboard_android.c
  8. 0 102
      packages/tui/clipboard/clipboard_android.go
  9. 0 80
      packages/tui/clipboard/clipboard_ios.go
  10. 0 20
      packages/tui/clipboard/clipboard_ios.m
  11. 0 336
      packages/tui/clipboard/clipboard_test.go
  12. 0 33
      packages/tui/clipboard/cmd/gclip-gui/AndroidManifest.xml
  13. 0 31
      packages/tui/clipboard/cmd/gclip-gui/README.md
  14. 0 236
      packages/tui/clipboard/cmd/gclip-gui/main.go
  15. 0 40
      packages/tui/clipboard/cmd/gclip/README.md
  16. 0 131
      packages/tui/clipboard/cmd/gclip/main.go
  17. 0 56
      packages/tui/clipboard/example_test.go
  18. 0 14
      packages/tui/clipboard/export_test.go
  19. 0 13
      packages/tui/clipboard/go.mod
  20. 0 8
      packages/tui/clipboard/go.sum
  21. 0 109
      packages/tui/clipboard/test_linux_nocgo.sh
  22. 0 15
      packages/tui/clipboard/tests/Makefile
  23. 0 11
      packages/tui/clipboard/tests/test-docker.sh
  24. BIN
      packages/tui/clipboard/tests/testdata/android.png
  25. BIN
      packages/tui/clipboard/tests/testdata/clipboard.png
  26. BIN
      packages/tui/clipboard/tests/testdata/darwin.png
  27. BIN
      packages/tui/clipboard/tests/testdata/ios.png
  28. BIN
      packages/tui/clipboard/tests/testdata/linux.png
  29. BIN
      packages/tui/clipboard/tests/testdata/windows.png
  30. 1 1
      packages/tui/cmd/opencode/main.go
  31. 2 8
      packages/tui/go.mod
  32. 0 4
      packages/tui/go.sum
  33. 1 1
      packages/tui/internal/app/app.go
  34. 1 1
      packages/tui/internal/clipboard/clipboard.go
  35. 1 1
      packages/tui/internal/clipboard/clipboard_darwin.go
  36. 1 1
      packages/tui/internal/clipboard/clipboard_linux.go
  37. 0 0
      packages/tui/internal/clipboard/clipboard_nocgo.go
  38. 0 0
      packages/tui/internal/clipboard/clipboard_windows.go
  39. 1 1
      packages/tui/internal/components/chat/editor.go
  40. BIN
      packages/tui/opencode

+ 0 - 12
packages/tui/clipboard/.github/FUNDING.yml

@@ -1,12 +0,0 @@
-# These are supported funding model platforms
-
-github: [changkun] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: # Replace with a single Patreon username
-open_collective: # Replace with a single Open Collective username
-ko_fi: # Replace with a single Ko-fi username
-tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
-community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
-issuehunt: # Replace with a single IssueHunt username
-otechie: # Replace with a single Otechie username
-custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

+ 0 - 71
packages/tui/clipboard/.github/workflows/clipboard.yml

@@ -1,71 +0,0 @@
-# Copyright 2021 The golang.design Initiative Authors.
-# All rights reserved. Use of this source code is governed
-# by a MIT license that can be found in the LICENSE file.
-#
-# Written by Changkun Ou <changkun.de>
-
-name: clipboard
-
-on:
-  push:
-    branches: [ main ]
-  pull_request:
-    branches: [ main ]
-
-jobs:
-  platform_test:
-    env:
-      DISPLAY: ':0.0'
-    runs-on: ${{ matrix.os }}
-    strategy:
-      fail-fast: false
-      matrix:
-        os: [ubuntu-latest, macos-latest, windows-latest]
-        go: ['1.24.x']
-    steps:
-    - name: Install and run dependencies (xvfb libx11-dev)
-      if: ${{ runner.os == 'Linux' }}
-      run: |
-        sudo apt update
-        sudo apt install -y xvfb libx11-dev x11-utils libegl1-mesa-dev libgles2-mesa-dev
-        Xvfb :0 -screen 0 1024x768x24 > /dev/null 2>&1 &
-        # Wait for Xvfb
-        MAX_ATTEMPTS=120 # About 60 seconds
-        COUNT=0
-        echo -n "Waiting for Xvfb to be ready..."
-        while ! xdpyinfo -display "${DISPLAY}" >/dev/null 2>&1; do
-          echo -n "."
-          sleep 0.50s
-          COUNT=$(( COUNT + 1 ))
-          if [ "${COUNT}" -ge "${MAX_ATTEMPTS}" ]; then
-            echo "  Gave up waiting for X server on ${DISPLAY}"
-            exit 1
-          fi
-        done
-        echo "Done - Xvfb is ready!"
-
-    - uses: actions/checkout@v2
-    - uses: actions/setup-go@v2
-      with:
-        stable: 'false'
-        go-version: ${{ matrix.go }}
-
-    - name: Build (${{ matrix.go }})
-      run: |
-        go build -o gclip cmd/gclip/main.go
-        go build -o gclip-gui cmd/gclip-gui/main.go
-
-    - name: Run Tests with CGO_ENABLED=1 (${{ matrix.go }})
-      if: ${{ runner.os == 'Linux' || runner.os == 'macOS'}}
-      run: |
-        CGO_ENABLED=1 go test -v -covermode=atomic .
-
-    - name: Run Tests with CGO_ENABLED=0 (${{ matrix.go }})
-      if: ${{ runner.os == 'Linux' || runner.os == 'macOS'}}
-      run: |
-        CGO_ENABLED=0 go test -v -covermode=atomic .
-
-    - name: Run Tests on Windows (${{ matrix.go }})
-      if: ${{ runner.os == 'Windows'}}
-      run: |
-        go test -v -covermode=atomic .

+ 0 - 15
packages/tui/clipboard/.gitignore

@@ -1,15 +0,0 @@
-# Binaries for programs and plugins
-*.exe
-*.exe~
-*.dll
-*.so
-*.dylib
-
-# Test binary, built with `go test -c`
-*.test
-
-# Output of the go coverage tool, specifically when used with LiteIDE
-*.out
-
-# Dependency directories (remove the comment below to include it)
-# vendor/

+ 0 - 13
packages/tui/clipboard/Dockerfile

@@ -1,13 +0,0 @@
-# Copyright 2021 The golang.design Initiative Authors.
-# All rights reserved. Use of this source code is governed
-# by a MIT license that can be found in the LICENSE file.
-#
-# Written by Changkun Ou <changkun.de>
-
-FROM golang:1.24
-RUN apt-get update && apt-get install -y \
-      xvfb libx11-dev libegl1-mesa-dev libgles2-mesa-dev \
-    && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
-WORKDIR /app
-COPY . .
-CMD [ "sh", "-c", "./tests/test-docker.sh" ]

+ 0 - 21
packages/tui/clipboard/LICENSE

@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2021 Changkun Ou <[email protected]>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.

+ 0 - 162
packages/tui/clipboard/README.md

@@ -1,162 +0,0 @@
-# clipboard [![PkgGoDev](https://pkg.go.dev/badge/golang.design/x/clipboard)](https://pkg.go.dev/golang.design/x/clipboard) ![](https://changkun.de/urlstat?mode=github&repo=golang-design/clipboard) ![clipboard](https://github.com/golang-design/clipboard/workflows/clipboard/badge.svg?branch=main)
-
-Cross platform (macOS/Linux/Windows/Android/iOS) clipboard package in Go
-
-```go
-import "golang.design/x/clipboard"
-```
-
-## Features
-
-- Cross platform supports: **macOS, Linux (X11), Windows, iOS, and Android**
-- Copy/paste UTF-8 text
-- Copy/paste PNG encoded images (Desktop-only)
-- Command `gclip` as a demo application
-- Mobile app `gclip-gui` as a demo application
-
-## API Usage
-
-Package clipboard provides cross platform clipboard access and supports
-macOS/Linux/Windows/Android/iOS platform. Before interacting with the
-clipboard, one must call Init to assert if it is possible to use this
-package:
-
-```go
-// Init returns an error if the package is not ready for use.
-err := clipboard.Init()
-if err != nil {
-      panic(err)
-}
-```
-
-The most common operations are `Read` and `Write`. To use them:
-
-```go
-// write/read text format data of the clipboard, and
-// the byte buffer regarding the text are UTF8 encoded.
-clipboard.Write(clipboard.FmtText, []byte("text data"))
-clipboard.Read(clipboard.FmtText)
-
-// write/read image format data of the clipboard, and
-// the byte buffer regarding the image are PNG encoded.
-clipboard.Write(clipboard.FmtImage, []byte("image data"))
-clipboard.Read(clipboard.FmtImage)
-```
-
-Note that read/write regarding image format assumes that the bytes are
-PNG encoded since it serves the alpha blending purpose that might be
-used in other graphical software.
-
-In addition, `clipboard.Write` returns a channel that can receive an
-empty struct as a signal, which indicates the corresponding write call
-to the clipboard is outdated, meaning the clipboard has been overwritten
-by others and the previously written data is lost. For instance:
-
-```go
-changed := clipboard.Write(clipboard.FmtText, []byte("text data"))
-
-select {
-case <-changed:
-      println(`"text data" is no longer available from clipboard.`)
-}
-```
-
-You can ignore the returning channel if you don't need this type of
-notification. Furthermore, when you need more than just knowing whether
-clipboard data is changed, use the watcher API:
-
-```go
-ch := clipboard.Watch(context.TODO(), clipboard.FmtText)
-for data := range ch {
-      // print out clipboard data whenever it is changed
-      println(string(data))
-}
-```
-
-## Demos
-
-- A command line tool `gclip` for command line clipboard accesses, see document [here](./cmd/gclip/README.md).
-- A GUI application `gclip-gui` for functionality verifications on mobile systems, see a document [here](./cmd/gclip-gui/README.md).
-
-
-## Command Usage
-
-`gclip` command offers the ability to interact with the system clipboard
-from the shell. To install:
-
-```bash
-$ go install golang.design/x/clipboard/cmd/gclip@latest
-```
-
-```bash
-$ gclip
-gclip is a command that provides clipboard interaction.
-
-usage: gclip [-copy|-paste] [-f <file>]
-
-options:
-  -copy
-        copy data to clipboard
-  -f string
-        source or destination to a given file path
-  -paste
-        paste data from clipboard
-
-examples:
-gclip -paste                    paste from clipboard and prints the content
-gclip -paste -f x.txt           paste from clipboard and save as text to x.txt
-gclip -paste -f x.png           paste from clipboard and save as image to x.png
-
-cat x.txt | gclip -copy         copy content from x.txt to clipboard
-gclip -copy -f x.txt            copy content from x.txt to clipboard
-gclip -copy -f x.png            copy x.png as image data to clipboard
-```
-
-If `-copy` is used, the command will exit when the data is no longer
-available from the clipboard. You can always send the command to the
-background using a shell `&` operator, for example:
-
-```bash
-$ cat x.txt | gclip -copy &
-```
-
-## Platform Specific Details
-
-This package spent efforts to provide cross platform abstraction regarding
-accessing system clipboards, but here are a few details you might need to know.
-
-### Dependency
-
-- macOS: require Cgo, no dependency
- - Linux: require X11 dev package. For instance, install `libx11-dev` or `xorg-dev` or `libX11-devel` to access X window system.
-   Wayland sessions are currently unsupported; running under Wayland
-   typically requires an XWayland bridge and `DISPLAY` to be set.
-- Windows: no Cgo, no dependency
-- iOS/Android: collaborate with [`gomobile`](https://golang.org/x/mobile)
-
-### Screenshot
-
-In general, when you need test your implementation regarding images,
-There are system level shortcuts to put screenshot image into your system clipboard:
-
-- On macOS, use `Ctrl+Shift+Cmd+4`
-- On Linux/Ubuntu, use `Ctrl+Shift+PrintScreen`
-- On Windows, use `Shift+Win+s`
-
-As described in the API documentation, the package supports read/write
-UTF8 encoded plain text or PNG encoded image data. Thus,
-the other types of data are not supported yet, i.e. undefined behavior.
-
-## Who is using this package?
-
-The main purpose of building this package is to support the
-[midgard](https://changkun.de/s/midgard) project, which offers
-clipboard-based features like universal clipboard service that syncs
-clipboard content across multiple systems, allocating public accessible
-for clipboard content, etc.
-
-To know more projects, check our [wiki](https://github.com/golang-design/clipboard/wiki) page.
-
-## License
-
-MIT | &copy; 2021 The golang.design Initiative Authors, written by [Changkun Ou](https://changkun.de).

+ 0 - 80
packages/tui/clipboard/clipboard_android.c

@@ -1,80 +0,0 @@
-// Copyright 2021 The golang.design Initiative Authors.
-// All rights reserved. Use of this source code is governed
-// by a MIT license that can be found in the LICENSE file.
-//
-// Written by Changkun Ou <changkun.de>
-
-//go:build android
-
-#include <android/log.h>
-#include <jni.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, \
-    "GOLANG.DESIGN/X/CLIPBOARD", __VA_ARGS__)
-
-static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
-	jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
-	if (m == 0) {
-		(*env)->ExceptionClear(env);
-		LOG_FATAL("cannot find method %s %s", name, sig);
-		return 0;
-	}
-	return m;
-}
-
-jobject get_clipboard(uintptr_t jni_env, uintptr_t ctx) {
-	JNIEnv *env = (JNIEnv*)jni_env;
-	jclass ctxClass = (*env)->GetObjectClass(env, (jobject)ctx);
-	jmethodID getSystemService = find_method(env, ctxClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
-
-	jstring service = (*env)->NewStringUTF(env, "clipboard");
-	jobject ret = (jobject)(*env)->CallObjectMethod(env, (jobject)ctx, getSystemService, service);
-	jthrowable err = (*env)->ExceptionOccurred(env);
-
-	if (err != NULL) {
-		LOG_FATAL("cannot find clipboard");
-		(*env)->ExceptionClear(env);
-		return NULL;
-	}
-	return ret;
-}
-
-char *clipboard_read_string(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx) {
-	JNIEnv *env = (JNIEnv*)jni_env;
-	jobject mgr = get_clipboard(jni_env, ctx);
-	if (mgr == NULL) {
-		return NULL;
-	}
-
-	jclass mgrClass = (*env)->GetObjectClass(env, mgr);
-	jmethodID getText = find_method(env, mgrClass, "getText", "()Ljava/lang/CharSequence;");
-
-	jobject content = (jstring)(*env)->CallObjectMethod(env, mgr, getText);
-	if (content == NULL) {
-		return NULL;
-	}
-
-	jclass clzCharSequence = (*env)->GetObjectClass(env, content);
-	jmethodID toString = (*env)->GetMethodID(env, clzCharSequence, "toString", "()Ljava/lang/String;");
-	jobject s = (*env)->CallObjectMethod(env, content, toString);
-
-	const char *chars = (*env)->GetStringUTFChars(env, s, NULL);
-	char *copy = strdup(chars);
-	(*env)->ReleaseStringUTFChars(env, s, chars);
-	return copy;
-}
-
-void clipboard_write_string(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx, char *str) {
-	JNIEnv *env = (JNIEnv*)jni_env;
-	jobject mgr = get_clipboard(jni_env, ctx);
-	if (mgr == NULL) {
-		return;
-	}
-
-	jclass mgrClass = (*env)->GetObjectClass(env, mgr);
-	jmethodID setText = find_method(env, mgrClass, "setText", "(Ljava/lang/CharSequence;)V");
-
-	(*env)->CallVoidMethod(env, mgr, setText, (*env)->NewStringUTF(env, str));
-}

+ 0 - 102
packages/tui/clipboard/clipboard_android.go

@@ -1,102 +0,0 @@
-// Copyright 2021 The golang.design Initiative Authors.
-// All rights reserved. Use of this source code is governed
-// by a MIT license that can be found in the LICENSE file.
-//
-// Written by Changkun Ou <changkun.de>
-
-//go:build android
-
-package clipboard
-
-/*
-#cgo LDFLAGS: -landroid -llog
-
-#include <stdlib.h>
-char *clipboard_read_string(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx);
-void clipboard_write_string(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx, char *str);
-
-*/
-import "C"
-import (
-	"bytes"
-	"context"
-	"time"
-	"unsafe"
-
-	"golang.org/x/mobile/app"
-)
-
-func initialize() error { return nil }
-
-func read(t Format) (buf []byte, err error) {
-	switch t {
-	case FmtText:
-		s := ""
-		if err := app.RunOnJVM(func(vm, env, ctx uintptr) error {
-			cs := C.clipboard_read_string(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx))
-			if cs == nil {
-				return nil
-			}
-
-			s = C.GoString(cs)
-			C.free(unsafe.Pointer(cs))
-			return nil
-		}); err != nil {
-			return nil, err
-		}
-		return []byte(s), nil
-	case FmtImage:
-		return nil, errUnsupported
-	default:
-		return nil, errUnsupported
-	}
-}
-
-// write writes the given data to clipboard and
-// returns true if success or false if failed.
-func write(t Format, buf []byte) (<-chan struct{}, error) {
-	done := make(chan struct{}, 1)
-	switch t {
-	case FmtText:
-		cs := C.CString(string(buf))
-		defer C.free(unsafe.Pointer(cs))
-
-		if err := app.RunOnJVM(func(vm, env, ctx uintptr) error {
-			C.clipboard_write_string(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx), cs)
-			done <- struct{}{}
-			return nil
-		}); err != nil {
-			return nil, err
-		}
-		return done, nil
-	case FmtImage:
-		return nil, errUnsupported
-	default:
-		return nil, errUnsupported
-	}
-}
-
-func watch(ctx context.Context, t Format) <-chan []byte {
-	recv := make(chan []byte, 1)
-	ti := time.NewTicker(time.Second)
-	last := Read(t)
-	go func() {
-		for {
-			select {
-			case <-ctx.Done():
-				close(recv)
-				return
-			case <-ti.C:
-				b := Read(t)
-				if b == nil {
-					continue
-				}
-				if bytes.Compare(last, b) != 0 {
-					recv <- b
-					last = b
-				}
-			}
-		}
-	}()
-	return recv
-}

+ 0 - 80
packages/tui/clipboard/clipboard_ios.go

@@ -1,80 +0,0 @@
-// Copyright 2021 The golang.design Initiative Authors.
-// All rights reserved. Use of this source code is governed
-// by a MIT license that can be found in the LICENSE file.
-//
-// Written by Changkun Ou <changkun.de>
-
-//go:build ios
-
-package clipboard
-
-/*
-#cgo CFLAGS: -x objective-c
-#cgo LDFLAGS: -framework Foundation -framework UIKit -framework MobileCoreServices
-
-#import <stdlib.h>
-void clipboard_write_string(char *s);
-char *clipboard_read_string();
-*/
-import "C"
-import (
-	"bytes"
-	"context"
-	"time"
-	"unsafe"
-)
-
-func initialize() error { return nil }
-
-func read(t Format) (buf []byte, err error) {
-	switch t {
-	case FmtText:
-		return []byte(C.GoString(C.clipboard_read_string())), nil
-	case FmtImage:
-		return nil, errUnsupported
-	default:
-		return nil, errUnsupported
-	}
-}
-
-// SetContent sets the clipboard content for iOS
-func write(t Format, buf []byte) (<-chan struct{}, error) {
-	done := make(chan struct{}, 1)
-	switch t {
-	case FmtText:
-		cs := C.CString(string(buf))
-		defer C.free(unsafe.Pointer(cs))
-
-		C.clipboard_write_string(cs)
-		return done, nil
-	case FmtImage:
-		return nil, errUnsupported
-	default:
-		return nil, errUnsupported
-	}
-}
-
-func watch(ctx context.Context, t Format) <-chan []byte {
-	recv := make(chan []byte, 1)
-	ti := time.NewTicker(time.Second)
-	last := Read(t)
-	go func() {
-		for {
-			select {
-			case <-ctx.Done():
-				close(recv)
-				return
-			case <-ti.C:
-				b := Read(t)
-				if b == nil {
-					continue
-				}
-				if bytes.Compare(last, b) != 0 {
-					recv <- b
-					last = b
-				}
-			}
-		}
-	}()
-	return recv
-}

+ 0 - 20
packages/tui/clipboard/clipboard_ios.m

@@ -1,20 +0,0 @@
-// Copyright 2021 The golang.design Initiative Authors.
-// All rights reserved. Use of this source code is governed
-// by a MIT license that can be found in the LICENSE file.
-//
-// Written by Changkun Ou <changkun.de>
-
-//go:build ios
-
-#import <UIKit/UIKit.h>
-#import <MobileCoreServices/MobileCoreServices.h>
-
-void clipboard_write_string(char *s) {
-    NSString *value = [NSString stringWithUTF8String:s];
-    [[UIPasteboard generalPasteboard] setString:value];
-}
-
-char *clipboard_read_string() {
-    NSString *str = [[UIPasteboard generalPasteboard] string];
-    return (char *)[str UTF8String];
-}

+ 0 - 336
packages/tui/clipboard/clipboard_test.go

@@ -1,336 +0,0 @@
-// Copyright 2021 The golang.design Initiative Authors.
-// All rights reserved. Use of this source code is governed
-// by a MIT license that can be found in the LICENSE file.
-//
-// Written by Changkun Ou <changkun.de>
-
-package clipboard_test
-
-import (
-	"bytes"
-	"context"
-	"errors"
-	"image/color"
-	"image/png"
-	"os"
-	"reflect"
-	"runtime"
-	"testing"
-	"time"
-
-	"golang.design/x/clipboard"
-)
-
-func init() {
-	clipboard.Debug = true
-}
-
-func TestClipboardInit(t *testing.T) {
-	t.Run("no-cgo", func(t *testing.T) {
-		if val, ok := os.LookupEnv("CGO_ENABLED"); !ok || val != "0" {
-			t.Skip("CGO_ENABLED is set to 1")
-		}
-		if runtime.GOOS == "windows" {
-			t.Skip("Windows does not need to check for cgo")
-		}
-
-		if err := clipboard.Init(); !errors.Is(err, clipboard.ErrCgoDisabled) {
-			t.Fatalf("expect ErrCgoDisabled, got: %v", err)
-		}
-	})
-	t.Run("with-cgo", func(t *testing.T) {
-		if val, ok := os.LookupEnv("CGO_ENABLED"); ok && val == "0" {
-			t.Skip("CGO_ENABLED is set to 0")
-		}
-		if runtime.GOOS != "linux" {
-			t.Skip("Only Linux may return error at the moment.")
-		}
-
-		if err := clipboard.Init(); err != nil && !errors.Is(err, clipboard.ErrUnavailable) {
-			t.Fatalf("expect ErrUnavailable, but got: %v", err)
-		}
-	})
-}
-
-func TestClipboard(t *testing.T) {
-	if runtime.GOOS != "windows" {
-		if val, ok := os.LookupEnv("CGO_ENABLED"); ok && val == "0" {
-			t.Skip("CGO_ENABLED is set to 0")
-		}
-	}
-
-	t.Run("image", func(t *testing.T) {
-		data, err := os.ReadFile("tests/testdata/clipboard.png")
-		if err != nil {
-			t.Fatalf("failed to read gold file: %v", err)
-		}
-		clipboard.Write(clipboard.FmtImage, data)
-
-		b := clipboard.Read(clipboard.FmtText)
-		if b != nil {
-			t.Fatalf("read clipboard that stores image data as text should fail, but got len: %d", len(b))
-		}
-
-		b = clipboard.Read(clipboard.FmtImage)
-		if b == nil {
-			t.Fatalf("read clipboard that stores image data as image should success, but got: nil")
-		}
-
-		img1, err := png.Decode(bytes.NewReader(data))
-		if err != nil {
-			t.Fatalf("write image is not png encoded: %v", err)
-		}
-		img2, err := png.Decode(bytes.NewReader(b))
-		if err != nil {
-			t.Fatalf("read image is not png encoded: %v", err)
-		}
-
-		w := img2.Bounds().Dx()
-		h := img2.Bounds().Dy()
-
-		incorrect := 0
-		for i := 0; i < w; i++ {
-			for j := 0; j < h; j++ {
-				wr, wg, wb, wa := img1.At(i, j).RGBA()
-				gr, gg, gb, ga := img2.At(i, j).RGBA()
-				want := color.RGBA{
-					R: uint8(wr),
-					G: uint8(wg),
-					B: uint8(wb),
-					A: uint8(wa),
-				}
-				got := color.RGBA{
-					R: uint8(gr),
-					G: uint8(gg),
-					B: uint8(gb),
-					A: uint8(ga),
-				}
-
-				if !reflect.DeepEqual(want, got) {
-					t.Logf("read data from clipbaord is inconsistent with previous written data, pix: (%d,%d), got: %+v, want: %+v", i, j, got, want)
-					incorrect++
-				}
-			}
-		}
-
-		if incorrect > 0 {
-			t.Fatalf("read data from clipboard contains too much inconsistent pixels to the previous written data, number of incorrect pixels: %v", incorrect)
-		}
-	})
-
-	t.Run("text", func(t *testing.T) {
-		data := []byte("golang.design/x/clipboard")
-		clipboard.Write(clipboard.FmtText, data)
-
-		b := clipboard.Read(clipboard.FmtImage)
-		if b != nil {
-			t.Fatalf("read clipboard that stores text data as image should fail, but got len: %d", len(b))
-		}
-		b = clipboard.Read(clipboard.FmtText)
-		if b == nil {
-			t.Fatal("read clipboard taht stores text data as text should success, but got: nil")
-		}
-
-		if !reflect.DeepEqual(data, b) {
-			t.Fatalf("read data from clipbaord is inconsistent with previous written data, got: %d, want: %d", len(b), len(data))
-		}
-	})
-}
-
-func TestClipboardMultipleWrites(t *testing.T) {
-	if runtime.GOOS != "windows" {
-		if val, ok := os.LookupEnv("CGO_ENABLED"); ok && val == "0" {
-			t.Skip("CGO_ENABLED is set to 0")
-		}
-	}
-
-	data, err := os.ReadFile("tests/testdata/clipboard.png")
-	if err != nil {
-		t.Fatalf("failed to read gold file: %v", err)
-	}
-	chg := clipboard.Write(clipboard.FmtImage, data)
-
-	data = []byte("golang.design/x/clipboard")
-	clipboard.Write(clipboard.FmtText, data)
-
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
-	defer cancel()
-
-	select {
-	case <-ctx.Done():
-		t.Fatalf("failed to receive clipboard change notification")
-	case _, ok := <-chg:
-		if !ok {
-			t.Fatalf("change channel is closed before receiving the changed clipboard data")
-		}
-	}
-	_, ok := <-chg
-	if ok {
-		t.Fatalf("changed channel should be closed after receiving the notification")
-	}
-
-	b := clipboard.Read(clipboard.FmtImage)
-	if b != nil {
-		t.Fatalf("read clipboard that should store text data as image should fail, but got: %d", len(b))
-	}
-
-	b = clipboard.Read(clipboard.FmtText)
-	if b == nil {
-		t.Fatalf("read clipboard that should store text data as text should success, got: nil")
-	}
-
-	if !reflect.DeepEqual(data, b) {
-		t.Fatalf("read data from clipbaord is inconsistent with previous write, want %s, got: %s", string(data), string(b))
-	}
-}
-
-func TestClipboardConcurrentRead(t *testing.T) {
-	if runtime.GOOS != "windows" {
-		if val, ok := os.LookupEnv("CGO_ENABLED"); ok && val == "0" {
-			t.Skip("CGO_ENABLED is set to 0")
-		}
-	}
-
-	// This test check that concurrent read/write to the clipboard does
-	// not cause crashes on some specific platform, such as macOS.
-	done := make(chan bool, 2)
-	go func() {
-		defer func() {
-			done <- true
-		}()
-		clipboard.Read(clipboard.FmtText)
-	}()
-	go func() {
-		defer func() {
-			done <- true
-		}()
-		clipboard.Read(clipboard.FmtImage)
-	}()
-	<-done
-	<-done
-}
-
-func TestClipboardWriteEmpty(t *testing.T) {
-	if runtime.GOOS != "windows" {
-		if val, ok := os.LookupEnv("CGO_ENABLED"); ok && val == "0" {
-			t.Skip("CGO_ENABLED is set to 0")
-		}
-	}
-
-	chg1 := clipboard.Write(clipboard.FmtText, nil)
-	if got := clipboard.Read(clipboard.FmtText); got != nil {
-		t.Fatalf("write nil to clipboard should read nil, got: %v", string(got))
-	}
-	clipboard.Write(clipboard.FmtText, []byte(""))
-	<-chg1
-
-	if got := clipboard.Read(clipboard.FmtText); string(got) != "" {
-		t.Fatalf("write empty string to clipboard should read empty string, got: `%v`", string(got))
-	}
-}
-
-func TestClipboardWatch(t *testing.T) {
-	if runtime.GOOS != "windows" {
-		if val, ok := os.LookupEnv("CGO_ENABLED"); ok && val == "0" {
-			t.Skip("CGO_ENABLED is set to 0")
-		}
-	}
-
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
-	defer cancel()
-
-	// clear clipboard
-	clipboard.Write(clipboard.FmtText, []byte(""))
-	lastRead := clipboard.Read(clipboard.FmtText)
-
-	changed := clipboard.Watch(ctx, clipboard.FmtText)
-
-	want := []byte("golang.design/x/clipboard")
-	go func(ctx context.Context) {
-		t := time.NewTicker(time.Millisecond * 500)
-		for {
-			select {
-			case <-ctx.Done():
-				return
-			case <-t.C:
-				clipboard.Write(clipboard.FmtText, want)
-			}
-		}
-	}(ctx)
-	for {
-		select {
-		case <-ctx.Done():
-			if string(lastRead) == "" {
-				t.Fatalf("clipboard watch never receives a notification")
-			}
-			t.Log(string(lastRead))
-			return
-		case data, ok := <-changed:
-			if !ok {
-				if string(lastRead) == "" {
-					t.Fatalf("clipboard watch never receives a notification")
-				}
-				return
-			}
-			if !bytes.Equal(data, want) {
-				t.Fatalf("received data from watch mismatch, want: %v, got %v", string(want), string(data))
-			}
-			lastRead = data
-		}
-	}
-}
-
-func BenchmarkClipboard(b *testing.B) {
-	b.Run("text", func(b *testing.B) {
-		data := []byte("golang.design/x/clipboard")
-
-		b.ReportAllocs()
-		b.ResetTimer()
-		for i := 0; i < b.N; i++ {
-			clipboard.Write(clipboard.FmtText, data)
-			_ = clipboard.Read(clipboard.FmtText)
-		}
-	})
-}
-
-func TestClipboardNoCgo(t *testing.T) {
-	if val, ok := os.LookupEnv("CGO_ENABLED"); !ok || val != "0" {
-		t.Skip("CGO_ENABLED is set to 1")
-	}
-	if runtime.GOOS == "windows" {
-		t.Skip("Windows should always be tested")
-	}
-
-	t.Run("Read", func(t *testing.T) {
-		defer func() {
-			if r := recover(); r != nil {
-				return
-			}
-			t.Fatalf("expect to fail when CGO_ENABLED=0")
-		}()
-
-		clipboard.Read(clipboard.FmtText)
-	})
-
-	t.Run("Write", func(t *testing.T) {
-		defer func() {
-			if r := recover(); r != nil {
-				return
-			}
-			t.Fatalf("expect to fail when CGO_ENABLED=0")
-		}()
-
-		clipboard.Write(clipboard.FmtText, []byte("dummy"))
-	})
-
-	t.Run("Watch", func(t *testing.T) {
-		defer func() {
-			if r := recover(); r != nil {
-				return
-			}
-			t.Fatalf("expect to fail when CGO_ENABLED=0")
-		}()
-
-		clipboard.Watch(context.TODO(), clipboard.FmtText)
-	})
-}

+ 0 - 33
packages/tui/clipboard/cmd/gclip-gui/AndroidManifest.xml

@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2021 The golang.design Initiative Authors.
-All rights reserved. Use of this source code is governed
-by a MIT license that can be found in the LICENSE file.
-
-Written by Changkun Ou <changkun.de>
--->
-<manifest
-	xmlns:android="http://schemas.android.com/apk/res/android"
-	package="design.golang.clipboard.gclip"
-	android:versionCode="1"
-	android:versionName="1.0">
-
-
-	<!-- In order to access the clipboard, the application manifest must
-	     specify the permission requirement. See the following page for
-	     details.
-	     http://developer.android.com/guide/topics/manifest/manifest-intro.html#perms -->
-	<uses-permission android:name="android.permission.CLIPBOARD" />
-
-	<application android:label="gclip" android:debuggable="true">
-	<activity android:name="org.golang.app.GoNativeActivity"
-		android:label="Gclip"
-		android:configChanges="orientation|keyboardHidden">
-		<meta-data android:name="android.app.lib_name" android:value="Gclip" />
-		<intent-filter>
-			<action android:name="android.intent.action.MAIN" />
-			<category android:name="android.intent.category.LAUNCHER" />
-		</intent-filter>
-	</activity>
-	</application>
-</manifest>

+ 0 - 31
packages/tui/clipboard/cmd/gclip-gui/README.md

@@ -1,31 +0,0 @@
-# gclip-gui
-
-This is a very basic example for verification purpose that demonstrates
-how the [golang.design/x/clipboard](https://golang.design/x/clipboard)
-can interact with macOS/Linux/Windows/Android/iOS system clipboard.
-
-The gclip GUI application writes a string to the system clipboard
-periodically then reads it back and renders it if possible.
-
-Because of the system limitation, on mobile devices, only string data is
-supported at the moment. Hence, one must use clipboard.FmtText. Other supplied
-formats result in a panic.
-
-This example is intentded as cross platform application. To build it, one
-must use [gomobile](https://golang.org/x/mobile). You may follow the instructions
-provided in the [GoMobile wiki](https://github.com/golang/go/wiki/Mobile) page.
-
-
-- For desktop: `go build -o gclip-gui`
-- For Android: `gomobile build -v -target=android -o gclip-gui.apk`
-- For iOS:     `gomobile build -v -target=ios -bundleid design.golang.gclip-gui.app`
-
-## Screenshots
-
-| macOS | iOS | Windows | Android | Linux |
-|:-----:|:---:|:-------:|:-------:|:-----:|
-|![](../../tests/testdata/darwin.png)|![](../../tests/testdata/ios.png)|![](../../tests/testdata/windows.png)|![](../../tests/testdata/android.png)|![](../../tests/testdata/linux.png)|
-
-## License
-
-MIT | &copy; 2021 The golang.design Initiative Authors, written by [Changkun Ou](https://changkun.de).

+ 0 - 236
packages/tui/clipboard/cmd/gclip-gui/main.go

@@ -1,236 +0,0 @@
-// Copyright 2021 The golang.design Initiative Authors.
-// All rights reserved. Use of this source code is governed
-// by a MIT license that can be found in the LICENSE file.
-//
-// Written by Changkun Ou <changkun.de>
-
-//go:build android || ios || linux || darwin || windows
-
-// This is a very basic example for verification purpose that
-// demonstrates how the golang.design/x/clipboard can interact
-// with macOS/Linux/Windows/Android/iOS system clipboard.
-//
-// The gclip GUI application writes a string to the system clipboard
-// periodically then reads it back and renders it if possible.
-//
-// Because of the system limitation, on mobile devices, only string
-// data is supported at the moment. Hence, one must use clipboard.FmtText.
-// Other supplied formats result in a panic.
-//
-// This example is intentded as cross platform application.
-// To build it, one must use gomobile (https://golang.org/x/mobile).
-// You may follow the instructions provided in the GoMobile's wiki page:
-// https://github.com/golang/go/wiki/Mobile.
-//
-// - For desktop:
-//
-// 	go build -o gclip-gui
-//
-// - For Android:
-//
-// 	gomobile build -v -target=android -o gclip-gui.apk
-//
-// - For iOS:
-//
-// 	gomobile build -v -target=ios -bundleid design.golang.gclip-gui.app
-//
-package main
-
-import (
-	"fmt"
-	"image"
-	"image/color"
-	"log"
-	"os"
-	"sync"
-	"time"
-
-	"golang.design/x/clipboard"
-
-	"golang.org/x/image/font"
-	"golang.org/x/image/font/basicfont"
-	"golang.org/x/image/math/fixed"
-	"golang.org/x/mobile/app"
-	"golang.org/x/mobile/event/lifecycle"
-	"golang.org/x/mobile/event/paint"
-	"golang.org/x/mobile/event/size"
-	"golang.org/x/mobile/exp/gl/glutil"
-	"golang.org/x/mobile/geom"
-	"golang.org/x/mobile/gl"
-)
-
-type Label struct {
-	sz     size.Event
-	images *glutil.Images
-	m      *glutil.Image
-	drawer *font.Drawer
-
-	mu   sync.Mutex
-	data string
-}
-
-func NewLabel(images *glutil.Images) *Label {
-	return &Label{
-		images: images,
-		data:   "Hello! Gclip.",
-		drawer: nil,
-	}
-}
-
-func (l *Label) SetLabel(s string) {
-	l.mu.Lock()
-	defer l.mu.Unlock()
-
-	l.data = s
-}
-
-const (
-	lineWidth  = 100
-	lineHeight = 120
-)
-
-func (l *Label) Draw(sz size.Event) {
-	l.mu.Lock()
-	s := l.data
-	l.mu.Unlock()
-	imgW, imgH := lineWidth*basicfont.Face7x13.Width, lineHeight*basicfont.Face7x13.Height
-	if sz.WidthPx == 0 && sz.HeightPx == 0 {
-		return
-	}
-	if imgW > sz.WidthPx {
-		imgW = sz.WidthPx
-	}
-
-	if l.sz != sz {
-		l.sz = sz
-		if l.m != nil {
-			l.m.Release()
-		}
-		l.m = l.images.NewImage(imgW, imgH)
-	}
-	// Clear the drawing image.
-	for i := 0; i < len(l.m.RGBA.Pix); i++ {
-		l.m.RGBA.Pix[i] = 0
-	}
-
-	l.drawer = &font.Drawer{
-		Dst:  l.m.RGBA,
-		Src:  image.NewUniform(color.RGBA{0, 100, 125, 255}),
-		Face: basicfont.Face7x13,
-		Dot:  fixed.P(5, 10),
-	}
-	l.drawer.DrawString(s)
-	l.m.Upload()
-	l.m.Draw(
-		sz,
-		geom.Point{X: 0, Y: 50},
-		geom.Point{X: geom.Pt(imgW), Y: 50},
-		geom.Point{X: 0, Y: geom.Pt(imgH)},
-		l.m.RGBA.Bounds(),
-	)
-}
-
-func (l *Label) Release() {
-	if l.m != nil {
-		l.m.Release()
-		l.m = nil
-		l.images = nil
-	}
-}
-
-// GclipApp is the application instance.
-type GclipApp struct {
-	app app.App
-
-	ctx gl.Context
-	siz size.Event
-
-	images *glutil.Images
-	l      *Label
-
-	counter int
-}
-
-// WatchClipboard watches the system clipboard every seconds.
-func (g *GclipApp) WatchClipboard() {
-	go func() {
-		tk := time.NewTicker(time.Second)
-		for range tk.C {
-			// Write something to the clipboard
-			w := fmt.Sprintf("(gclip: %d)", g.counter)
-			clipboard.Write(clipboard.FmtText, []byte(w))
-			g.counter++
-			log.Println(w)
-
-			// Read it back and render it, if possible.
-			data := clipboard.Read(clipboard.FmtText)
-			if len(data) == 0 {
-				continue
-			}
-
-			// Set the current clipboard data as label content and render on the screen.
-			r := fmt.Sprintf("clipboard: %s", string(data))
-			g.l.SetLabel(r)
-			g.app.Send(paint.Event{})
-		}
-	}()
-}
-
-func (g *GclipApp) OnStart(e lifecycle.Event) {
-	g.ctx, _ = e.DrawContext.(gl.Context)
-	g.images = glutil.NewImages(g.ctx)
-	g.l = NewLabel(g.images)
-	g.app.Send(paint.Event{})
-}
-
-func (g *GclipApp) OnStop() {
-	g.l.Release()
-	g.images.Release()
-	g.ctx = nil
-}
-
-func (g *GclipApp) OnSize(size size.Event) {
-	g.siz = size
-}
-
-func (g *GclipApp) OnDraw() {
-	if g.ctx == nil {
-		return
-	}
-	defer g.app.Send(paint.Event{})
-	defer g.app.Publish()
-	g.ctx.ClearColor(0, 0, 0, 1)
-	g.ctx.Clear(gl.COLOR_BUFFER_BIT)
-	g.l.Draw(g.siz)
-}
-
-func init() {
-	err := clipboard.Init()
-	if err != nil {
-		panic(err)
-	}
-}
-
-func main() {
-	app.Main(func(a app.App) {
-		gclip := GclipApp{app: a}
-		gclip.app.Send(size.Event{WidthPx: 800, HeightPx: 500})
-		gclip.WatchClipboard()
-		for e := range gclip.app.Events() {
-			switch e := gclip.app.Filter(e).(type) {
-			case lifecycle.Event:
-				switch e.Crosses(lifecycle.StageVisible) {
-				case lifecycle.CrossOn:
-					gclip.OnStart(e)
-				case lifecycle.CrossOff:
-					gclip.OnStop()
-					os.Exit(0)
-				}
-			case size.Event:
-				gclip.OnSize(e)
-			case paint.Event:
-				gclip.OnDraw()
-			}
-		}
-	})
-}

+ 0 - 40
packages/tui/clipboard/cmd/gclip/README.md

@@ -1,40 +0,0 @@
-# gclip
-
-`gclip` command offers the ability to interact with the system clipboard
-from the shell. To install:
-
-```bash
-$ go install golang.design/x/clipboard/cmd/gclip@latest
-```
-
-```bash
-$ gclip
-gclip is a command that provides clipboard interaction.
-usage: gclip [-copy|-paste] [-f <file>]
-options:
-  -copy
-        copy data to clipboard
-  -f string
-        source or destination to a given file path
-  -paste
-        paste data from clipboard
-examples:
-gclip -paste                    paste from clipboard and prints the content
-gclip -paste -f x.txt           paste from clipboard and save as text to x.txt
-gclip -paste -f x.png           paste from clipboard and save as image to x.png
-cat x.txt | gclip -copy         copy content from x.txt to clipboard
-gclip -copy -f x.txt            copy content from x.txt to clipboard
-gclip -copy -f x.png            copy x.png as image data to clipboard
-```
-
-If `-copy` is used, the command will exit when the data is no longer
-available from the clipboard. You can always send the command to the
-background using a shell `&` operator, for example:
-
-```bash
-$ cat x.txt | gclip -copy &
-```
-
-## License
-
-MIT | &copy; 2021 The golang.design Initiative Authors, written by [Changkun Ou](https://changkun.de).

+ 0 - 131
packages/tui/clipboard/cmd/gclip/main.go

@@ -1,131 +0,0 @@
-// Copyright 2021 The golang.design Initiative Authors.
-// All rights reserved. Use of this source code is governed
-// by a MIT license that can be found in the LICENSE file.
-//
-// Written by Changkun Ou <changkun.de>
-
-package main // go install golang.design/x/clipboard/cmd/gclip@latest
-
-import (
-	"flag"
-	"fmt"
-	"io"
-	"os"
-	"path/filepath"
-
-	"golang.design/x/clipboard"
-)
-
-func usage() {
-	fmt.Fprintf(os.Stderr, `gclip is a command that provides clipboard interaction.
-
-usage: gclip [-copy|-paste] [-f <file>]
-
-options:
-`)
-	flag.PrintDefaults()
-	fmt.Fprintf(os.Stderr, `
-examples:
-gclip -paste                    paste from clipboard and prints the content
-gclip -paste -f x.txt           paste from clipboard and save as text to x.txt
-gclip -paste -f x.png           paste from clipboard and save as image to x.png
-
-cat x.txt | gclip -copy         copy content from x.txt to clipboard
-gclip -copy -f x.txt            copy content from x.txt to clipboard
-gclip -copy -f x.png            copy x.png as image data to clipboard
-`)
-	os.Exit(2)
-}
-
-var (
-	in   = flag.Bool("copy", false, "copy data to clipboard")
-	out  = flag.Bool("paste", false, "paste data from clipboard")
-	file = flag.String("f", "", "source or destination to a given file path")
-)
-
-func init() {
-	err := clipboard.Init()
-	if err != nil {
-		panic(err)
-	}
-}
-
-func main() {
-	flag.Usage = usage
-	flag.Parse()
-	if *out {
-		if err := pst(); err != nil {
-			usage()
-		}
-		return
-	}
-	if *in {
-		if err := cpy(); err != nil {
-			usage()
-		}
-		return
-	}
-	usage()
-}
-
-func cpy() error {
-	t := clipboard.FmtText
-	ext := filepath.Ext(*file)
-
-	switch ext {
-	case ".png":
-		t = clipboard.FmtImage
-	case ".txt":
-		fallthrough
-	default:
-		t = clipboard.FmtText
-	}
-
-	var (
-		b   []byte
-		err error
-	)
-	if *file != "" {
-		b, err = os.ReadFile(*file)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "failed to read given file: %v", err)
-			return err
-		}
-	} else {
-		b, err = io.ReadAll(os.Stdin)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "failed to read from stdin: %v", err)
-			return err
-		}
-	}
-
-	// Wait until clipboard content has been changed.
-	<-clipboard.Write(t, b)
-	return nil
-}
-
-func pst() (err error) {
-	var b []byte
-
-	b = clipboard.Read(clipboard.FmtText)
-	if b == nil {
-		b = clipboard.Read(clipboard.FmtImage)
-	}
-
-	if *file != "" && b != nil {
-		err = os.WriteFile(*file, b, os.ModePerm)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "failed to write data to file %s: %v", *file, err)
-		}
-		return err
-	}
-
-	for len(b) > 0 {
-		n, err := os.Stdout.Write(b)
-		if err != nil {
-			return err
-		}
-		b = b[n:]
-	}
-	return nil
-}

+ 0 - 56
packages/tui/clipboard/example_test.go

@@ -1,56 +0,0 @@
-// Copyright 2021 The golang.design Initiative Authors.
-// All rights reserved. Use of this source code is governed
-// by a MIT license that can be found in the LICENSE file.
-//
-// Written by Changkun Ou <changkun.de>
-
-//go:build cgo
-
-package clipboard_test
-
-import (
-	"context"
-	"fmt"
-	"time"
-
-	"golang.design/x/clipboard"
-)
-
-func ExampleWrite() {
-	err := clipboard.Init()
-	if err != nil {
-		panic(err)
-	}
-
-	clipboard.Write(clipboard.FmtText, []byte("Hello, 世界"))
-	// Output:
-}
-
-func ExampleRead() {
-	err := clipboard.Init()
-	if err != nil {
-		panic(err)
-	}
-
-	fmt.Println(string(clipboard.Read(clipboard.FmtText)))
-	// Output:
-	// Hello, 世界
-}
-
-func ExampleWatch() {
-	err := clipboard.Init()
-	if err != nil {
-		panic(err)
-	}
-
-	ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
-	defer cancel()
-
-	changed := clipboard.Watch(context.Background(), clipboard.FmtText)
-	go func(ctx context.Context) {
-		clipboard.Write(clipboard.FmtText, []byte("你好,world"))
-	}(ctx)
-	fmt.Println(string(<-changed))
-	// Output:
-	// 你好,world
-}

+ 0 - 14
packages/tui/clipboard/export_test.go

@@ -1,14 +0,0 @@
-// Copyright 2021 The golang.design Initiative Authors.
-// All rights reserved. Use of this source code is governed
-// by a MIT license that can be found in the LICENSE file.
-//
-// Written by Changkun Ou <changkun.de>
-
-package clipboard
-
-// for debugging errors
-var (
-	Debug          = debug
-	ErrUnavailable = errUnavailable
-	ErrCgoDisabled = errNoCgo
-)

+ 0 - 13
packages/tui/clipboard/go.mod

@@ -1,13 +0,0 @@
-module golang.design/x/clipboard
-
-go 1.24
-
-require (
-	golang.org/x/image v0.28.0
-	golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f
-)
-
-require (
-	golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 // indirect
-	golang.org/x/sys v0.33.0 // indirect
-)

+ 0 - 8
packages/tui/clipboard/go.sum

@@ -1,8 +0,0 @@
-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/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
-golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=

+ 0 - 109
packages/tui/clipboard/test_linux_nocgo.sh

@@ -1,109 +0,0 @@
-#!/bin/bash
-# Test script for Linux CGO-free clipboard implementation
-
-echo "Testing Linux clipboard implementation without CGO..."
-
-# Check for required tools
-echo "Checking for clipboard tools..."
-for tool in xclip xsel wl-copy; do
-    if command -v $tool &> /dev/null; then
-        echo "✓ $tool is installed"
-    else
-        echo "✗ $tool is not installed"
-    fi
-done
-
-# Create test program
-cat > test_linux_clipboard.go << 'EOF'
-package main
-
-import (
-    "fmt"
-    "log"
-    "os"
-    "golang.design/x/clipboard"
-)
-
-func main() {
-    err := clipboard.Init()
-    if err != nil {
-        log.Fatal("Failed to initialize clipboard:", err)
-    }
-    
-    // Test text
-    fmt.Println("\n=== Testing Text Clipboard ===")
-    testText := []byte("Hello from CGO-free Linux clipboard!")
-    clipboard.Write(clipboard.FmtText, testText)
-    fmt.Println("Wrote text:", string(testText))
-    
-    readText := clipboard.Read(clipboard.FmtText)
-    fmt.Println("Read text:", string(readText))
-    
-    if string(testText) == string(readText) {
-        fmt.Println("✓ Text clipboard test passed")
-    } else {
-        fmt.Println("✗ Text clipboard test failed")
-    }
-    
-    // Test empty write
-    fmt.Println("\n=== Testing Empty Write ===")
-    clipboard.Write(clipboard.FmtText, []byte{})
-    emptyRead := clipboard.Read(clipboard.FmtText)
-    if emptyRead == nil || len(emptyRead) == 0 {
-        fmt.Println("✓ Empty write test passed")
-    } else {
-        fmt.Println("✗ Empty write test failed, got:", string(emptyRead))
-    }
-    
-    // Test image if requested
-    if len(os.Args) > 1 && os.Args[1] == "image" {
-        fmt.Println("\n=== Testing Image Clipboard ===")
-        
-        // Try to read test image
-        imageData, err := os.ReadFile("tests/testdata/clipboard.png")
-        if err != nil {
-            fmt.Println("Could not read test image:", err)
-            return
-        }
-        
-        clipboard.Write(clipboard.FmtImage, imageData)
-        fmt.Println("Wrote image data, length:", len(imageData))
-        
-        readImage := clipboard.Read(clipboard.FmtImage)
-        if readImage != nil {
-            fmt.Println("Read image data, length:", len(readImage))
-            if len(imageData) == len(readImage) {
-                fmt.Println("✓ Image clipboard test passed")
-            } else {
-                fmt.Println("✗ Image lengths don't match")
-            }
-        } else {
-            fmt.Println("✗ Failed to read image from clipboard")
-        }
-        
-        // Test that reading text from image clipboard returns nil
-        textFromImage := clipboard.Read(clipboard.FmtText)
-        if textFromImage == nil {
-            fmt.Println("✓ Reading text from image clipboard correctly returned nil")
-        } else {
-            fmt.Println("✗ Reading text from image clipboard should return nil, got:", string(textFromImage))
-        }
-    }
-}
-EOF
-
-# Run tests with CGO disabled
-echo -e "\n=== Running with CGO_ENABLED=0 ==="
-CGO_ENABLED=0 go run test_linux_clipboard.go
-
-echo -e "\n=== Running with CGO_ENABLED=0 and image test ==="
-CGO_ENABLED=0 go run test_linux_clipboard.go image
-
-# Run actual tests
-echo -e "\n=== Running go test with CGO_ENABLED=0 ==="
-CGO_ENABLED=0 go test -v -run TestClipboard
-
-# Clean up
-rm -f test_linux_clipboard.go
-
-echo -e "\nTest script completed!"

+ 0 - 15
packages/tui/clipboard/tests/Makefile

@@ -1,15 +0,0 @@
-# Copyright 2021 The golang.design Initiative Authors.
-# All rights reserved. Use of this source code is governed
-# by a MIT license that can be found in the LICENSE file.
-#
-# Written by Changkun Ou <changkun.de>
-
-all: test
-
-test:
-	go test -v -count=1 -covermode=atomic ..
-
-test-docker:
-	docker build -t golang-design/x/clipboard ..
-	docker run --rm --name cb golang-design/x/clipboard
-	docker rmi golang-design/x/clipboard

+ 0 - 11
packages/tui/clipboard/tests/test-docker.sh

@@ -1,11 +0,0 @@
-# Copyright 2021 The golang.design Initiative Authors.
-# All rights reserved. Use of this source code is governed
-# by a MIT license that can be found in the LICENSE file.
-#
-# Written by Changkun Ou <changkun.de>
-
-# require apt-get install xvfb
-Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
-export DISPLAY=:99.0
-
-go test -v -covermode=atomic ./... 

BIN
packages/tui/clipboard/tests/testdata/android.png


BIN
packages/tui/clipboard/tests/testdata/clipboard.png


BIN
packages/tui/clipboard/tests/testdata/darwin.png


BIN
packages/tui/clipboard/tests/testdata/ios.png


BIN
packages/tui/clipboard/tests/testdata/linux.png


BIN
packages/tui/clipboard/tests/testdata/windows.png


+ 1 - 1
packages/tui/cmd/opencode/main.go

@@ -13,8 +13,8 @@ import (
 	"github.com/sst/opencode-sdk-go"
 	"github.com/sst/opencode-sdk-go/option"
 	"github.com/sst/opencode/internal/app"
+	"github.com/sst/opencode/internal/clipboard"
 	"github.com/sst/opencode/internal/tui"
-	"golang.design/x/clipboard"
 )
 
 var Version = "dev"

+ 2 - 8
packages/tui/go.mod

@@ -17,14 +17,11 @@ require (
 	github.com/muesli/termenv v0.16.0
 	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
 	github.com/sst/opencode-sdk-go v0.1.0-alpha.8
-	golang.design/x/clipboard v0.7.1
+	golang.org/x/image v0.28.0
 	rsc.io/qr v0.2.0
 )
 
-replace (
-	github.com/sst/opencode-sdk-go => ./sdk
-	golang.design/x/clipboard => ./clipboard
-)
+replace github.com/sst/opencode-sdk-go => ./sdk
 
 require golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
 
@@ -58,8 +55,6 @@ require (
 	github.com/tidwall/pretty v1.2.1 // indirect
 	github.com/tidwall/sjson v1.2.5 // indirect
 	github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
-	golang.org/x/exp/shiny v0.0.0-20250620022241-b7579e27df2b // indirect
-	golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f // indirect
 	golang.org/x/mod v0.25.0 // indirect
 	golang.org/x/tools v0.34.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
@@ -87,7 +82,6 @@ require (
 	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
 	github.com/yuin/goldmark v1.7.8 // indirect
 	github.com/yuin/goldmark-emoji v1.0.5 // indirect
-	golang.org/x/image v0.28.0 // indirect
 	golang.org/x/net v0.41.0 // indirect
 	golang.org/x/sync v0.15.0 // indirect
 	golang.org/x/sys v0.33.0 // indirect

+ 0 - 4
packages/tui/go.sum

@@ -216,12 +216,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
 golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
-golang.org/x/exp/shiny v0.0.0-20250620022241-b7579e27df2b h1:zELBzk+7ERc6m8BxhzU2VYjp03wlEvi+cIgYQR5H3CI=
-golang.org/x/exp/shiny v0.0.0-20250620022241-b7579e27df2b/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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=

+ 1 - 1
packages/tui/internal/app/app.go

@@ -12,13 +12,13 @@ import (
 
 	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/sst/opencode-sdk-go"
+	"github.com/sst/opencode/internal/clipboard"
 	"github.com/sst/opencode/internal/commands"
 	"github.com/sst/opencode/internal/components/toast"
 	"github.com/sst/opencode/internal/config"
 	"github.com/sst/opencode/internal/styles"
 	"github.com/sst/opencode/internal/theme"
 	"github.com/sst/opencode/internal/util"
-	"golang.design/x/clipboard"
 )
 
 type App struct {

+ 1 - 1
packages/tui/clipboard/clipboard.go → packages/tui/internal/clipboard/clipboard.go

@@ -53,7 +53,7 @@ clipboard data is changed, use the watcher API:
 		println(string(data))
 	}
 */
-package clipboard // import "golang.design/x/clipboard"
+package clipboard
 
 import (
 	"context"

+ 1 - 1
packages/tui/clipboard/clipboard_darwin.go → packages/tui/internal/clipboard/clipboard_darwin.go

@@ -4,7 +4,7 @@
 //
 // Written by Changkun Ou <changkun.de>
 
-//go:build darwin && !ios
+//go:build darwin
 
 package clipboard
 

+ 1 - 1
packages/tui/clipboard/clipboard_linux.go → packages/tui/internal/clipboard/clipboard_linux.go

@@ -4,7 +4,7 @@
 //
 // Written by Changkun Ou <changkun.de>
 
-//go:build linux && !android
+//go:build linux
 
 package clipboard
 

+ 0 - 0
packages/tui/clipboard/clipboard_nocgo.go → packages/tui/internal/clipboard/clipboard_nocgo.go


+ 0 - 0
packages/tui/clipboard/clipboard_windows.go → packages/tui/internal/clipboard/clipboard_windows.go


+ 1 - 1
packages/tui/internal/components/chat/editor.go

@@ -15,13 +15,13 @@ import (
 	"github.com/google/uuid"
 	"github.com/sst/opencode-sdk-go"
 	"github.com/sst/opencode/internal/app"
+	"github.com/sst/opencode/internal/clipboard"
 	"github.com/sst/opencode/internal/commands"
 	"github.com/sst/opencode/internal/components/dialog"
 	"github.com/sst/opencode/internal/components/textarea"
 	"github.com/sst/opencode/internal/styles"
 	"github.com/sst/opencode/internal/theme"
 	"github.com/sst/opencode/internal/util"
-	"golang.design/x/clipboard"
 )
 
 type EditorComponent interface {

BIN
packages/tui/opencode