defmodule PrymnWeb.Terminal do use PrymnWeb, :live_component alias PrymnProto.Prymn.TerminalRequest @impl true def mount(socket) do {:ok, assign(socket, :open, false)} end @impl true def update(assigns, socket) do socket = if assigns[:data], do: push_event(socket, "data", %{"data" => assigns[:data]}), else: socket {:ok, assign(socket, assigns)} end @impl true def render(assigns) do ~H"""
Open Terminal Close Terminal
""" end @impl true def handle_event("open_terminal", _params, socket) do agent = Prymn.Agents.from_server(socket.assigns.server) pid = self() Task.Supervisor.start_child(Prymn.TaskSupervisor, fn -> # FIXME: Have to wrap this in a Task because gun sends unsolicited messages # to calling process stream = Prymn.Agents.terminal(agent) {:ok, mux_pid} = Task.Supervisor.start_child(Prymn.TaskSupervisor, fn -> receive_loop(stream) end) send_update(pid, PrymnWeb.Terminal, id: "terminal", mux_pid: mux_pid, open: true) case GRPC.Stub.recv(stream, timeout: :infinity) do {:ok, stream} -> Enum.map(stream, fn {:ok, %{output: data}} -> send(mux_pid, :data) send_update(pid, PrymnWeb.Terminal, id: "terminal", data: data) {:error, _err} -> send_update(pid, PrymnWeb.Terminal, id: "terminal", open: false) end) {:error, error} -> dbg(error) end end) {:noreply, socket} end def handle_event("close_terminal", _params, socket) do send(socket.assigns.mux_pid, :close) {:noreply, assign(socket, :open, false)} end def handle_event("data_event", data, socket) when is_binary(data) do send(socket.assigns.mux_pid, {:data_event, data}) {:noreply, socket} end def handle_event("resize_event", %{"cols" => cols, "rows" => rows}, socket) do send(socket.assigns.mux_pid, {:resize_event, rows, cols}) {:noreply, socket} end defp receive_loop(stream) do receive do {:data_event, data} -> GRPC.Stub.send_request(stream, %TerminalRequest{input: data}) receive_loop(stream) {:resize_event, rows, cols} -> GRPC.Stub.send_request(stream, %TerminalRequest{ resize: %TerminalRequest.Resize{rows: rows, cols: cols} }) receive_loop(stream) :data -> receive_loop(stream) :close -> GRPC.Stub.send_request(stream, %TerminalRequest{input: ""}, end_stream: true) after 120_000 -> GRPC.Stub.send_request(stream, %TerminalRequest{input: ""}, end_stream: true) end end end