defmodule LivebookWeb.SessionLive.AttachedLive do use LivebookWeb, :live_view alias Livebook.{Session, Runtime, Utils} @impl true def mount(_params, %{"session" => session, "current_runtime" => current_runtime}, socket) do if connected?(socket) do Phoenix.PubSub.subscribe(Livebook.PubSub, "sessions:#{session.id}") end {:ok, assign(socket, session: session, current_runtime: current_runtime, error_message: nil, data: initial_data(current_runtime) )} end @impl true def render(assigns) do ~H"""
<%= if @error_message do %>
<%= @error_message %>
<% end %>

Connect the session to an already running node and evaluate code in the context of that node. Thanks to this approach you can work with an arbitrary Elixir runtime. Make sure to give the node a name and a cookie, for example:

<%= if longname = Livebook.Config.longname() do %>
iex --name test@<%= longname %> --cookie mycookie
<% else %>
iex --sname test --cookie mycookie
<% end %>

Then enter the connection information below:

<.form let={f} for={:data} phx-submit="init" phx-change="validate" autocomplete="off" spellcheck="false">
Name
<%= text_input f, :name, value: @data["name"], class: "input", placeholder: name_placeholder() %>
Cookie
<%= text_input f, :cookie, value: @data["cookie"], class: "input", placeholder: "mycookie" %>
""" end defp matching_runtime?(%Runtime.Attached{} = runtime, data) do initial_data(runtime) == data end defp matching_runtime?(_runtime, _data), do: false @impl true def handle_event("validate", %{"data" => data}, socket) do {:noreply, assign(socket, data: data)} end def handle_event("init", %{"data" => data}, socket) do node = Utils.node_from_name(data["name"]) cookie = String.to_atom(data["cookie"]) case Runtime.Attached.init(node, cookie) do {:ok, runtime} -> Session.connect_runtime(socket.assigns.session.pid, runtime) {:noreply, assign(socket, data: initial_data(runtime), error_message: nil)} {:error, error} -> message = runtime_error_to_message(error) {:noreply, assign(socket, data: data, error_message: message)} end end @impl true def handle_info({:operation, {:set_runtime, _pid, runtime}}, socket) do {:noreply, assign(socket, current_runtime: runtime)} end def handle_info(_message, socket), do: {:noreply, socket} defp initial_data(%Runtime.Attached{node: node, cookie: cookie}) do %{ "name" => Atom.to_string(node), "cookie" => Atom.to_string(cookie) } end defp initial_data(_runtime), do: %{"name" => "", "cookie" => ""} defp data_valid?(data) do data["name"] != "" and data["cookie"] != "" end defp name_placeholder do if longname = Livebook.Config.longname(), do: "test@#{longname}", else: "test" end defp runtime_error_to_message(:unreachable), do: "Node unreachable" end