diff --git a/assets/css/js_interop.css b/assets/css/js_interop.css index 8b5f1c92c..58aad3efe 100644 --- a/assets/css/js_interop.css +++ b/assets/css/js_interop.css @@ -109,6 +109,11 @@ solely client-side operations. @apply hidden; } +[data-element="session"]:not([data-js-side-panel-content="runtime-info"]) + [data-element="runtime-info"] { + @apply hidden; +} + [data-element="session"][data-js-side-panel-content="sections-list"] [data-element="sections-list-toggle"] { @apply text-gray-50 bg-gray-700; @@ -119,6 +124,11 @@ solely client-side operations. @apply text-gray-50 bg-gray-700; } +[data-element="session"][data-js-side-panel-content="runtime-info"] + [data-element="runtime-info-toggle"] { + @apply text-gray-50 bg-gray-700; +} + [data-element="section-headline"]:not(:hover) [data-element="section-name"]:not(:focus) + [data-element="section-actions"]:not(:focus-within) { diff --git a/assets/css/tooltips.css b/assets/css/tooltips.css index d41f66dea..a64f0915b 100644 --- a/assets/css/tooltips.css +++ b/assets/css/tooltips.css @@ -32,13 +32,13 @@ Example usage: white-space: pre; text-align: center; display: block; + z-index: 100; background-color: #1c273c; color: #f0f5f9; font-size: 12px; font-weight: 500; border-radius: 4px; padding: 3px 12px; - z-index: 100; visibility: hidden; transition-property: visibility; transition-duration: 0s; @@ -50,6 +50,7 @@ Example usage: content: ""; position: absolute; display: block; + z-index: 100; /* For the arrow we use the triangle trick: https://css-tricks.com/snippets/css/css-triangle/ */ border-width: var(--arrow-size); border-style: solid; diff --git a/assets/js/session/index.js b/assets/js/session/index.js index 8e7003267..10629afae 100644 --- a/assets/js/session/index.js +++ b/assets/js/session/index.js @@ -119,6 +119,10 @@ const Session = { toggleClientsList(this); }); + getRuntimeInfoToggle().addEventListener("click", (event) => { + toggleRuntimeInfo(this); + }); + getNotebook().addEventListener("scroll", (event) => { updateSectionListHighlight(); }); @@ -335,7 +339,7 @@ function handleDocumentKeyDown(hook, event) { } else if (keyBuffer.tryMatch(["s", "u"])) { toggleClientsList(hook); } else if (keyBuffer.tryMatch(["s", "r"])) { - showNotebookRuntimeSettings(hook); + toggleRuntimeInfo(hook); } else if (keyBuffer.tryMatch(["s", "b"])) { showBin(hook); } else if (keyBuffer.tryMatch(["e", "x"])) { @@ -581,23 +585,23 @@ function updateSectionListHighlight() { // User action handlers (mostly keybindings) function toggleSectionsList(hook) { - if (hook.el.getAttribute("data-js-side-panel-content") === "sections-list") { - hook.el.removeAttribute("data-js-side-panel-content"); - } else { - hook.el.setAttribute("data-js-side-panel-content", "sections-list"); - } + toggleSidePanelContent(hook, "sections-list"); } function toggleClientsList(hook) { - if (hook.el.getAttribute("data-js-side-panel-content") === "clients-list") { - hook.el.removeAttribute("data-js-side-panel-content"); - } else { - hook.el.setAttribute("data-js-side-panel-content", "clients-list"); - } + toggleSidePanelContent(hook, "clients-list"); } -function showNotebookRuntimeSettings(hook) { - hook.pushEvent("show_runtime_settings", {}); +function toggleRuntimeInfo(hook) { + toggleSidePanelContent(hook, "runtime-info"); +} + +function toggleSidePanelContent(hook, name) { + if (hook.el.getAttribute("data-js-side-panel-content") === name) { + hook.el.removeAttribute("data-js-side-panel-content"); + } else { + hook.el.setAttribute("data-js-side-panel-content", name); + } } function showBin(hook) { @@ -1024,6 +1028,10 @@ function getClientsListToggle() { return document.querySelector(`[data-element="clients-list-toggle"]`); } +function getRuntimeInfoToggle() { + return document.querySelector(`[data-element="runtime-info-toggle"]`); +} + function cancelEvent(event) { // Cancel any default browser behavior. event.preventDefault(); diff --git a/lib/livebook_web/live/session_live.ex b/lib/livebook_web/live/session_live.ex index 519f291e2..a49defd15 100644 --- a/lib/livebook_web/live/session_live.ex +++ b/lib/livebook_web/live/session_live.ex @@ -37,7 +37,8 @@ defmodule LivebookWeb.SessionLive do platform: platform, self: self(), data_view: data_to_view(data), - autofocus_cell_id: autofocus_cell_id(data.notebook) + autofocus_cell_id: autofocus_cell_id(data.notebook), + empty_default_runtime: Livebook.Config.default_runtime() |> elem(0) |> struct() ) |> assign_private(data: data) |> allow_upload(:cell_image, @@ -87,11 +88,10 @@ defmodule LivebookWeb.SessionLive do icon="group-fill" label="Connected users (su)" data_element="clients-list-toggle" /> - + data_element="runtime-info-toggle" />
-
-

- Sections -

-
- <%= for section_item <- @data_view.sections_items do %> -
- - <.session_status status={elem(section_item.status, 0)} cell_id={elem(section_item.status, 1)} /> -
- <% end %> -
- -
+ <.sections_list data_view={@data_view} />
-
-
-

- Users -

- - - <%= length(@data_view.clients) %> connected - -
-
- <%= for {client_pid, user} <- @data_view.clients do %> -
- - <%= if client_pid != @self do %> - - - - - - - <% end %> -
- <% end %> -
-
+ <.clients_list data_view={@data_view} self={@self} /> +
+
+ <.runtime_info data_view={@data_view} session={@session} socket={@socket} empty_default_runtime={@empty_default_runtime} />
@@ -355,6 +287,143 @@ defmodule LivebookWeb.SessionLive do """ end + defp sections_list(assigns) do + ~H""" +
+

+ Sections +

+
+ <%= for section_item <- @data_view.sections_items do %> +
+ + <.session_status status={elem(section_item.status, 0)} cell_id={elem(section_item.status, 1)} /> +
+ <% end %> +
+ +
+ """ + end + + defp clients_list(assigns) do + ~H""" +
+
+

+ Users +

+ + + <%= length(@data_view.clients) %> connected + +
+
+ <%= for {client_pid, user} <- @data_view.clients do %> +
+ + <%= if client_pid != @self do %> + + + + + + + <% end %> +
+ <% end %> +
+
+ """ + end + + defp runtime_info(assigns) do + ~H""" +
+

+ Runtime +

+
+ <%= if @data_view.runtime do %> +
+ <.labeled_text label="Type" text={runtime_type_label(@data_view.runtime)} /> + <.labeled_text label="Node name" text={@data_view.runtime.node} /> +
+
+
+ + +
+ <%= live_patch to: Routes.session_path(@socket, :runtime_settings, @session.id), + class: "button button-gray button-square-icon", + type: "button" do %> + <.remix_icon icon="settings-3-line" /> + <% end %> +
+ <% else %> +
+ <.labeled_text label="Type" text={runtime_type_label(@empty_default_runtime)} /> +
+
+ + <%= live_patch to: Routes.session_path(@socket, :runtime_settings, @session.id), + class: "button button-gray button-square-icon", + type: "button" do %> + <.remix_icon icon="settings-3-line" /> + <% end %> +
+ <% end %> +
+
+ """ + end + + defp runtime_type_label(%Runtime.ElixirStandalone{}), do: "Elixir standalone" + defp runtime_type_label(%Runtime.MixStandalone{}), do: "Mix standalone" + defp runtime_type_label(%Runtime.Attached{}), do: "Attached" + defp runtime_type_label(%Runtime.Embedded{}), do: "Embedded" + defp session_status(%{status: :evaluating} = assigns) do ~H"""
<% end %> -

Connect the session to an already running node and evaluate code in the context of that node. diff --git a/lib/livebook_web/live/session_live/elixir_standalone_live.ex b/lib/livebook_web/live/session_live/elixir_standalone_live.ex index ea307f066..7520bd2d9 100644 --- a/lib/livebook_web/live/session_live/elixir_standalone_live.ex +++ b/lib/livebook_web/live/session_live/elixir_standalone_live.ex @@ -2,7 +2,6 @@ defmodule LivebookWeb.SessionLive.ElixirStandaloneLive do use LivebookWeb, :live_view alias Livebook.{Session, Runtime} - alias LivebookWeb.SessionLive.RuntimeHelpers @impl true def mount(_params, %{"session" => session, "current_runtime" => current_runtime}, socket) do @@ -22,7 +21,6 @@ defmodule LivebookWeb.SessionLive.ElixirStandaloneLive do <%= @error_message %> <% end %> -

Start a new local node to handle code evaluation.

diff --git a/lib/livebook_web/live/session_live/embedded_live.ex b/lib/livebook_web/live/session_live/embedded_live.ex index ba104dd16..6d3c7855f 100644 --- a/lib/livebook_web/live/session_live/embedded_live.ex +++ b/lib/livebook_web/live/session_live/embedded_live.ex @@ -2,7 +2,6 @@ defmodule LivebookWeb.SessionLive.EmbeddedLive do use LivebookWeb, :live_view alias Livebook.{Session, Runtime} - alias LivebookWeb.SessionLive.RuntimeHelpers @impl true def mount(_params, %{"session" => session, "current_runtime" => current_runtime}, socket) do @@ -17,7 +16,6 @@ defmodule LivebookWeb.SessionLive.EmbeddedLive do def render(assigns) do ~H"""
-

Run the notebook code within the Livebook node itself. This is reserved for specific cases where there is no option diff --git a/lib/livebook_web/live/session_live/mix_standalone_live.ex b/lib/livebook_web/live/session_live/mix_standalone_live.ex index a5ad24e05..eb7460037 100644 --- a/lib/livebook_web/live/session_live/mix_standalone_live.ex +++ b/lib/livebook_web/live/session_live/mix_standalone_live.ex @@ -2,7 +2,6 @@ defmodule LivebookWeb.SessionLive.MixStandaloneLive do use LivebookWeb, :live_view alias Livebook.{Session, Runtime, Utils, FileSystem} - alias LivebookWeb.SessionLive.RuntimeHelpers @type status :: :initial | :initializing | :finished @@ -27,7 +26,6 @@ defmodule LivebookWeb.SessionLive.MixStandaloneLive do def render(assigns) do ~H"""

-

Start a new local node in the context of a Mix project. This way all your code and dependencies will be available diff --git a/lib/livebook_web/live/session_live/runtime_component.ex b/lib/livebook_web/live/session_live/runtime_component.ex index 175e6f009..3d7d32351 100644 --- a/lib/livebook_web/live/session_live/runtime_component.ex +++ b/lib/livebook_web/live/session_live/runtime_component.ex @@ -1,7 +1,7 @@ defmodule LivebookWeb.SessionLive.RuntimeComponent do use LivebookWeb, :live_component - alias Livebook.{Session, Runtime} + alias Livebook.Runtime @impl true def mount(socket) do @@ -31,31 +31,11 @@ defmodule LivebookWeb.SessionLive.RuntimeComponent do @impl true def render(assigns) do ~H""" -

+

Runtime

-

- The code is evaluated in a separate Elixir runtime (node), - which you can configure yourself here. -

-
- <%= if @runtime do %> - <.labeled_text label="Type" text={runtime_type_label(@runtime)} /> - <.labeled_text label="Node name" text={@runtime.node} /> - - <% else %> -

- No connected node -

- <% end %> -
<.choice_button active={@type == "elixir_standalone"} @@ -96,11 +76,6 @@ defmodule LivebookWeb.SessionLive.RuntimeComponent do """ end - defp runtime_type_label(%Runtime.ElixirStandalone{}), do: "Elixir standalone" - defp runtime_type_label(%Runtime.MixStandalone{}), do: "Mix standalone" - defp runtime_type_label(%Runtime.Attached{}), do: "Attached" - defp runtime_type_label(%Runtime.Embedded{}), do: "Embedded" - defp runtime_type(%Runtime.ElixirStandalone{}), do: "elixir_standalone" defp runtime_type(%Runtime.MixStandalone{}), do: "mix_standalone" defp runtime_type(%Runtime.Attached{}), do: "attached" @@ -115,10 +90,4 @@ defmodule LivebookWeb.SessionLive.RuntimeComponent do def handle_event("set_runtime_type", %{"type" => type}, socket) do {:noreply, assign(socket, type: type)} end - - def handle_event("disconnect", _params, socket) do - Session.disconnect_runtime(socket.assigns.session.pid) - - {:noreply, socket} - end end diff --git a/lib/livebook_web/live/session_live/runtime_helpers.ex b/lib/livebook_web/live/session_live/runtime_helpers.ex deleted file mode 100644 index 0f242f9cd..000000000 --- a/lib/livebook_web/live/session_live/runtime_helpers.ex +++ /dev/null @@ -1,22 +0,0 @@ -defmodule LivebookWeb.SessionLive.RuntimeHelpers do - use Phoenix.Component - - @doc """ - Displays an info text if `@module` is the default runtime. - """ - def default_runtime_note(assigns) do - ~H""" - <%= if default_runtime_module?(@module) do %> -

- Note: This is the default runtime and starts - automatically as soon as you evaluate the first cell. -

- <% end %> - """ - end - - defp default_runtime_module?(module) do - {default_module, _args} = Livebook.Config.default_runtime() - default_module == module - end -end