diff --git a/src/snek/static/chat-input.js b/src/snek/static/chat-input.js index 8ff77fd..28e271b 100644 --- a/src/snek/static/chat-input.js +++ b/src/snek/static/chat-input.js @@ -176,7 +176,9 @@ class ChatInputComponent extends HTMLElement { }); this.appendChild(this.uploadButton); - + this.textarea.addEventListener("blur", () => { + this.updateFromInput(""); + }); this.textarea.addEventListener("keyup", (e) => { if (e.key === "Enter" && !e.shiftKey) { diff --git a/src/snek/static/container.js b/src/snek/static/container.js index 7a2c2ab..a17b37a 100644 --- a/src/snek/static/container.js +++ b/src/snek/static/container.js @@ -17,15 +17,20 @@ export class Container extends EventHandler{ this._container = el this.terminal.open(this._container) - this.terminal.onData(data => this.ws.send(new TextEncoder().encode(data))); + + this.terminal.onData(data => { + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + this.ws.send(data); + } + }); + } - this._fitAddon.fit(); this.refresh() - + this.terminal.focus() } refresh(){ - this._fitAddon.fit(); - this.terminal.write("\x0C"); + //this._fitAddon.fit(); + this.ws.send("\x0C"); } toggle(){ this._container.classList.toggle("hidden") @@ -34,7 +39,10 @@ export class Container extends EventHandler{ constructor(channelUid,log){ super() - this.terminal = new Terminal({ cursorBlink: true }); + this.terminal = new Terminal({ cursorBlink: true ,theme: { + background: 'rgba(0, 0, 0, 0)', // Fully transparent + } + }); this._fitAddon = new FitAddon.FitAddon(); this.terminal.loadAddon(this._fitAddon); window.addEventListener("resize", () => this._fitAddon.fit()); diff --git a/src/snek/system/docker.py b/src/snek/system/docker.py index 7cfa8f1..58eae25 100644 --- a/src/snek/system/docker.py +++ b/src/snek/system/docker.py @@ -3,6 +3,8 @@ import json import yaml import asyncio import subprocess +import pty +import os class ComposeFileManager: def __init__(self, compose_path="docker-compose.yml",event_handler=None): @@ -31,17 +33,19 @@ class ComposeFileManager: return False proc = self.running_instances.get(container_name) if not proc: - return False + return False + async def reader(event_handler,stream): + loop = asyncio.get_event_loop() while True: - line = await stream.read(1024) + line = await loop.run_in_executor(None,os.read,stream,1024) print("XXX",line) if not line: break await event_handler(container_name,"stdout",line) await self.stop(container_name) - asyncio.create_task(reader(self.event_handler,proc.stdout)) - asyncio.create_task(reader(self.event_handler,proc.stderr)) + asyncio.create_task(reader(self.event_handler,proc['master'])) + def create_instance( @@ -123,7 +127,7 @@ class ComposeFileManager: if not proc: return False try: - proc.stdin.write(data.encode()) + os.write(proc['master'], data.encode()) return True except Exception as ex: print(ex) @@ -178,13 +182,16 @@ class ComposeFileManager: if proc.returncode != 0: print(f"Failed to start {name}: {stderr.decode(errors='ignore')}") return False - + + master, slave = pty.openpty() proc = await asyncio.create_subprocess_exec( "docker", "compose", "-f", self.compose_path, "exec", name, "/bin/bash", - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, + stdin=slave, + stdout=slave, + stderr=slave, ) + + proc = {'proc':proc,'master':master,'slave':slave} # stdin,stderr = await proc.communicate() self.running_instances[name] = proc #if stdout: diff --git a/src/snek/templates/app.html b/src/snek/templates/app.html index 6569a19..e0b3a5d 100644 --- a/src/snek/templates/app.html +++ b/src/snek/templates/app.html @@ -54,7 +54,28 @@