Skip to content
Open
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: 1 addition & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/kr/pty"
"github.com/maxmcd/webtty/pkg/sd"
"github.com/mitchellh/colorstring"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
"golang.org/x/crypto/ssh/terminal"
)

Expand Down
2 changes: 1 addition & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"testing"

"github.com/kr/pty"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
"golang.org/x/crypto/ssh/terminal"
)

Expand Down
36 changes: 31 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
module github.com/maxmcd/webtty

go 1.12
go 1.23.0

toolchain go1.24.3

require (
github.com/btcsuite/btcutil v0.0.0-20190316010144-3ac1210f4b38
github.com/kr/pty v1.1.4
github.com/btcsuite/btcutil v1.0.2
github.com/kr/pty v1.1.8
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
github.com/pion/webrtc/v3 v3.1.29
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
github.com/pion/webrtc/v4 v4.1.1
golang.org/x/crypto v0.38.0
)

require (
github.com/creack/pty v1.1.24 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/pion/datachannel v1.5.10 // indirect
github.com/pion/dtls/v3 v3.0.6 // indirect
github.com/pion/ice/v4 v4.0.10 // indirect
github.com/pion/interceptor v0.1.38 // indirect
github.com/pion/logging v0.2.3 // indirect
github.com/pion/mdns/v2 v2.0.7 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.15 // indirect
github.com/pion/rtp v1.8.16 // indirect
github.com/pion/sctp v1.8.39 // indirect
github.com/pion/sdp/v3 v3.0.13 // indirect
github.com/pion/srtp/v3 v3.0.4 // indirect
github.com/pion/stun/v3 v3.0.0 // indirect
github.com/pion/transport/v3 v3.0.7 // indirect
github.com/pion/turn/v4 v4.0.2 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
)
211 changes: 71 additions & 140 deletions go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion host.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"github.com/kr/pty"
"github.com/maxmcd/webtty/pkg/sd"
"github.com/mitchellh/colorstring"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
)

type hostSession struct {
Expand Down
2 changes: 1 addition & 1 deletion host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"testing"

"github.com/kr/pty"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
)

func TestHosttDataChannelOnMessage(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion session.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"os"

"github.com/maxmcd/webtty/pkg/sd"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v4"
"golang.org/x/crypto/ssh/terminal"
)

Expand Down
28 changes: 19 additions & 9 deletions web-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"type": "git",
"url": "git+https://github.com/maxmcd/webtty.git"
},
"main": "src/app.ts",
"default": "src/app.ts",
"scripts": {
"build": "npm run go-build && parcel build src/index.html --public-url .",
"go-build": "mkdir -p ./dist/ && touch ./dist/foo && rm ./dist/* && GOOS=js GOARCH=wasm go build -o ./dist/main.wasm ./src",
Expand All @@ -17,15 +17,25 @@
"author": "Max McDonnell",
"license": "MIT",
"dependencies": {
"@babel/runtime-corejs2": "^7.12.5",
"gh-pages": "^2.0.1",
"parcel-bundler": "^1.10.3",
"xterm": "3.8.1"
"@babel/runtime-corejs2": "^7.27.4",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "5.5.0",
"gh-pages": "^6.3.0",
"parcel": "^2.15.2"
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/plugin-transform-runtime": "^7.12.10",
"cssnano": "^4.1.10",
"typescript": "^3.9.7"
"@babel/core": "^7.27.4",
"@babel/plugin-transform-runtime": "^7.27.4",
"buffer": "^6.0.3",
"crypto-browserify": "^3.12.1",
"cssnano": "^7.0.7",
"events": "^3.3.0",
"os-browserify": "^0.3.0",
"process": "^0.11.10",
"stream-browserify": "^3.0.0",
"string_decoder": "^1.3.0",
"typescript": "^5.8.3",
"util": "^0.12.5",
"vm-browserify": "^1.1.2"
}
}
100 changes: 100 additions & 0 deletions web-client/src/AttachAddon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* Copyright (c) 2014, 2019 The xterm.js authors. All rights reserved.
* @license MIT
*
* Implements the attach method, that attaches the terminal to a RTCDataChannel stream.
*/

import type { Terminal, IDisposable, ITerminalAddon } from '@xterm/xterm';
// import type { AttachAddon as IAttachApi } from '@xterm/addon-attach';

interface IAttachOptions {
bidirectional?: boolean;
}

export class AttachAddon implements ITerminalAddon {
private _rtcChannel: RTCDataChannel;
private _bidirectional: boolean;
private _disposables: IDisposable[] = [];

constructor(rtcChannel: RTCDataChannel, options?: IAttachOptions) {
this._rtcChannel = rtcChannel;
// always set binary type to arraybuffer, we do not handle blobs
this._rtcChannel.binaryType = 'arraybuffer';
this._bidirectional = !(options && options.bidirectional === false);
}

public activate(terminal: Terminal): void {
this._disposables.push(
addRtcChannelListener(this._rtcChannel, 'message', ev => {
const data: ArrayBuffer | string = ev.data;
terminal.write(typeof data === 'string' ? data : new Uint8Array(data));
})
);

if (this._bidirectional) {
this._disposables.push(terminal.onData(data => this._sendData(data)));
this._disposables.push(terminal.onBinary(data => this._sendBinary(data)));
}

this._disposables.push(addRtcChannelListener(this._rtcChannel, 'close', () => this.dispose()));
this._disposables.push(addRtcChannelListener(this._rtcChannel, 'error', () => this.dispose()));
}

public dispose(): void {
for (const d of this._disposables) {
d.dispose();
}
}

public setSize = (size: { rows: number; cols: number }) => {
this._rtcChannel.send(JSON.stringify(["set_size", size.rows, size.cols]));
};

private _sendData(data: string): void {
if (!this._checkOpenRtcChannel()) {
return;
}
this._rtcChannel.send(JSON.stringify(["stdin", data]));
}

private _sendBinary(data: string): void {
if (!this._checkOpenRtcChannel()) {
return;
}
const buffer = new Uint8Array(data.length);
for (let i = 0; i < data.length; ++i) {
buffer[i] = data.charCodeAt(i) & 255;
}
this._rtcChannel.send(buffer);
}

private _checkOpenRtcChannel(): boolean {
switch (this._rtcChannel.readyState) {
case "open":
return true;
case "connecting":
throw new Error('Attach addon was loaded before rtcChannel was open');
case "closing":
console.warn('Attach addon rtcChannel is closing');
return false;
case "closed":
throw new Error('Attach addon rtcChannel is closed');
default:
throw new Error('Unexpected rtcChannel state');
}
}
}

function addRtcChannelListener<K extends keyof RTCDataChannelEventMap>(rtcChannel: RTCDataChannel, type: K, handler: (this: RTCDataChannel, ev: RTCDataChannelEventMap[K]) => any): IDisposable {
rtcChannel.addEventListener(type, handler);
return {
dispose: () => {
if (!handler) {
// Already disposed
return;
}
rtcChannel.removeEventListener(type, handler);
}
};
}
36 changes: 21 additions & 15 deletions web-client/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import Terminal from "xterm/src/xterm.ts";
import * as attach from "./attach.ts";
import * as fullscreen from "xterm/src/addons/fullscreen/fullscreen.ts";
import * as fit from "xterm/src/addons/fit/fit.ts";
import { Terminal } from "@xterm/xterm";
import { AttachAddon } from "./AttachAddon";
import { FitAddon } from "@xterm/addon-fit";

import "xterm/dist/xterm.css";
import "xterm/dist/addons/fullscreen/fullscreen.css";
import "@xterm/xterm/css/xterm.css";

// imports "Go"
import "./wasm_exec.js";

Terminal.applyAddon(attach);
Terminal.applyAddon(fullscreen);
Terminal.applyAddon(fit);
const term = new Terminal();

var attachAddon : null | AttachAddon = null;

const fitAddon = new FitAddon();
term.loadAddon(fitAddon);

// Polyfill for WebAssembly on Safari
if (!WebAssembly.instantiateStreaming) {
Expand Down Expand Up @@ -71,12 +72,14 @@ const startSession = (data: string) => {

let TenKbSiteLoc = null;

const term = new Terminal();
term.open(document.getElementById("terminal"));
term.toggleFullScreen();
term.fit();
fitAddon.fit();
window.onresize = () => {
term.fit();
fitAddon.fit();
if (attachAddon) {
const dimensions = fitAddon.proposeDimensions();
dimensions ? attachAddon.setSize(dimensions) : null;
}
};
term.write("Welcome to the WebTTY web client.\n\r");

Expand All @@ -96,7 +99,10 @@ let sendChannel = pc.createDataChannel("data");
sendChannel.onclose = () => console.log("sendChannel has closed");
sendChannel.onopen = () => {
term.reset();
term.terminadoAttach(sendChannel);

attachAddon = new AttachAddon(sendChannel);
term.loadAddon(attachAddon);

sendChannel.send(JSON.stringify(["set_size", term.rows, term.cols]));
console.log("sendChannel has opened");
};
Expand Down Expand Up @@ -155,7 +161,7 @@ if (firstInput == false) {
term.write("Run webtty and paste the offer message below:\n\r");
}

term.on("data", data => {
term.onData(data => {
if (!firstInput) {
term.reset();
try {
Expand Down
Loading