From 23edacbcf52d2acf1087e540b88779941b25cde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20L=C3=A9one?= Date: Thu, 21 Aug 2025 12:31:55 +0200 Subject: [PATCH 1/2] term: allow compiling for js/wasm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a platform-specific implementation to allow the module to compile for GOOS=js and GOARCH=wasm, without providing terminal functionality. Terminal detection reports false, and unsupported operations return errors instead of silently succeeding. This is particularly important for operations such as disabling terminal echo, where silently ignoring the request could expose sensitive input. Actual WebAssembly terminal support can be considered separately. Signed-off-by: Rémy Léone Signed-off-by: Sebastiaan van Stijn --- term_unix.go | 2 +- term_webassembly.go | 57 +++++++++++++++++++++++++++++++++++++++++++++ termios_nonbsd.go | 3 +-- termios_unix.go | 2 +- 4 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 term_webassembly.go diff --git a/term_unix.go b/term_unix.go index 251a04f..1aace68 100644 --- a/term_unix.go +++ b/term_unix.go @@ -1,4 +1,4 @@ -//go:build !windows +//go:build !js && !windows package term diff --git a/term_webassembly.go b/term_webassembly.go new file mode 100644 index 0000000..0fcac5f --- /dev/null +++ b/term_webassembly.go @@ -0,0 +1,57 @@ +//go:build js + +package term + +import ( + "errors" + "io" + "os" +) + +var errUnsupported = errors.New("terminal operations are not supported on js") + +// terminalState holds the platform-specific state / console mode for the terminal. +type terminalState struct{} + +func stdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { + return os.Stdin, os.Stdout, os.Stderr +} + +func getFdInfo(in any) (uintptr, bool) { + if file, ok := in.(*os.File); ok { + return file.Fd(), false + } + return 0, false +} + +func getWinsize(fd uintptr) (*Winsize, error) { + return nil, errUnsupported +} + +func setWinsize(fd uintptr, ws *Winsize) error { + return errUnsupported +} + +func isTerminal(fd uintptr) bool { + return false +} + +func restoreTerminal(fd uintptr, state *State) error { + return errUnsupported +} + +func saveState(fd uintptr) (*State, error) { + return nil, errUnsupported +} + +func disableEcho(fd uintptr, state *State) error { + return errUnsupported +} + +func setRawTerminal(fd uintptr) (*State, error) { + return nil, errUnsupported +} + +func setRawTerminalOutput(uintptr) (*State, error) { + return nil, errUnsupported +} diff --git a/termios_nonbsd.go b/termios_nonbsd.go index 88b7b21..270a0e9 100644 --- a/termios_nonbsd.go +++ b/termios_nonbsd.go @@ -1,5 +1,4 @@ -//go:build !darwin && !freebsd && !netbsd && !openbsd && !windows -// +build !darwin,!freebsd,!netbsd,!openbsd,!windows +//go:build !darwin && !freebsd && !netbsd && !openbsd && !js && !windows package term diff --git a/termios_unix.go b/termios_unix.go index a94eebb..d98e4bd 100644 --- a/termios_unix.go +++ b/termios_unix.go @@ -1,4 +1,4 @@ -//go:build !windows +//go:build !js && !windows package term From 14bbe5c808d8924ecbfd3667f2e70b8476649262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20L=C3=A9one?= Date: Thu, 21 Aug 2025 12:31:55 +0200 Subject: [PATCH 2/2] feat: add support for webassembly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Léone Signed-off-by: Sebastiaan van Stijn --- term_webassembly.go | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/term_webassembly.go b/term_webassembly.go index 0fcac5f..13c492d 100644 --- a/term_webassembly.go +++ b/term_webassembly.go @@ -6,6 +6,7 @@ import ( "errors" "io" "os" + "syscall/js" ) var errUnsupported = errors.New("terminal operations are not supported on js") @@ -18,40 +19,56 @@ func stdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { } func getFdInfo(in any) (uintptr, bool) { + var inFd uintptr + var isTerminalIn bool if file, ok := in.(*os.File); ok { - return file.Fd(), false + inFd = file.Fd() + isTerminalIn = isTerminal(inFd) } - return 0, false + return inFd, isTerminalIn } func getWinsize(fd uintptr) (*Winsize, error) { - return nil, errUnsupported + window := js.Global().Get("window") + width := uint16(window.Get("innerWidth").Int()) + height := uint16(window.Get("innerHeight").Int()) + return &Winsize{Width: width, Height: height}, nil } func setWinsize(fd uintptr, ws *Winsize) error { - return errUnsupported + window := js.Global().Get("window") + window.Set("innerWidth", ws.Width) + window.Set("innerHeight", ws.Height) + + return nil } func isTerminal(fd uintptr) bool { - return false + return true } func restoreTerminal(fd uintptr, state *State) error { - return errUnsupported + if state == nil { + return errors.New("invalid terminal state") + } + return nil } func saveState(fd uintptr) (*State, error) { - return nil, errUnsupported + return &State{}, nil } func disableEcho(fd uintptr, state *State) error { - return errUnsupported + if state == nil { + return errors.New("invalid terminal state") + } + return nil } func setRawTerminal(fd uintptr) (*State, error) { - return nil, errUnsupported + return &State{}, nil } -func setRawTerminalOutput(uintptr) (*State, error) { +func setRawTerminalOutput(fd uintptr) (*State, error) { return nil, errUnsupported }