defmodule PrymnWeb.Terminal do use PrymnWeb, :live_component @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""" <div> <PrymnWeb.Button.primary :if={not @open} type="button" phx-target={@myself} phx-click="open_terminal" > Open Terminal </PrymnWeb.Button.primary> <PrymnWeb.Button.primary :if={@open} type="button" phx-target={@myself} phx-click="close_terminal" > Close Terminal </PrymnWeb.Button.primary> <div :if={@open} class="mt-2 bg-black p-2"> <div phx-hook="Terminal" id="terminal" /> </div> </div> """ 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