From f3206d9791fb7eb054a9dd4405b3143123435cbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 9 Feb 2024 11:58:20 +0100 Subject: [PATCH] Redesign deploy and runtime panels (#2478) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonatan KÅ‚osko --- assets/css/js_interop.css | 5 - assets/css/tooltips.css | 20 +- lib/livebook_web/components/app_components.ex | 6 +- .../components/layout_components.ex | 2 +- lib/livebook_web/live/apps_dashboard_live.ex | 26 +- .../live/session_live/app_docker_component.ex | 4 +- .../live/session_live/app_info_component.ex | 290 ++++++++++-------- .../session_live/app_settings_component.ex | 24 +- .../session_live/elixir_standalone_live.ex | 2 +- lib/livebook_web/live/session_live/render.ex | 96 +++--- test/livebook_web/live/session_live_test.exs | 6 +- 11 files changed, 277 insertions(+), 204 deletions(-) diff --git a/assets/css/js_interop.css b/assets/css/js_interop.css index 366c4a75a..1b06bb680 100644 --- a/assets/css/js_interop.css +++ b/assets/css/js_interop.css @@ -279,11 +279,6 @@ solely client-side operations. @apply text-gray-50 bg-gray-700; } -[data-el-session][data-js-side-panel-content="app-info"] - [data-el-app-indicator] { - @apply border-gray-700; -} - [data-el-clients-list-item]:not([data-js-followed]) [data-meta="unfollow"] { @apply hidden; } diff --git a/assets/css/tooltips.css b/assets/css/tooltips.css index 8a4e34c36..de4ee162d 100644 --- a/assets/css/tooltips.css +++ b/assets/css/tooltips.css @@ -14,6 +14,7 @@ Example usage: display: flex; --distance: 4px; --arrow-size: 5px; + --arrow-side-offset: 10px; --show-delay: 0.5s; } @@ -82,7 +83,17 @@ otherwise there's a tiny space between them. transform: translate(-50%, calc(1px - var(--arrow-size) - var(--distance))); } -.tooltip.top:after { +.tooltip.top-right:before { + bottom: 100%; + left: 50%; + transform: translate( + calc(0px - var(--arrow-side-offset)), + calc(1px - var(--arrow-size) - var(--distance)) + ); +} + +.tooltip.top:after, +.tooltip.top-right:after { bottom: 100%; left: 50%; transform: translate(-50%, calc(0px - var(--distance))); @@ -100,8 +111,11 @@ otherwise there's a tiny space between them. .tooltip.bottom-left:before { top: 100%; - right: 0; - transform: translate(0%, calc(var(--arrow-size) - 1px + var(--distance))); + left: 50%; + transform: translate( + calc(-100% + var(--arrow-side-offset)), + calc(var(--arrow-size) - 1px + var(--distance)) + ); } .tooltip.bottom:after, diff --git a/lib/livebook_web/components/app_components.ex b/lib/livebook_web/components/app_components.ex index 508b3e053..8d146be8f 100644 --- a/lib/livebook_web/components/app_components.ex +++ b/lib/livebook_web/components/app_components.ex @@ -68,15 +68,15 @@ defmodule LivebookWeb.AppComponents do @doc """ Shows a confirmation modal and closes the app on confirm. """ - def confirm_app_termination(socket, app_pid) do + def confirm_app_termination(socket, app_pid, title \\ "app") do on_confirm = fn socket -> Livebook.App.close(app_pid) socket end confirm(socket, on_confirm, - title: "Terminate app", - description: "All app sessions will be immediately terminated.", + title: "Terminate #{title}", + description: "All #{title} sessions will be immediately terminated.", confirm_text: "Terminate", confirm_icon: "delete-bin-6-line" ) diff --git a/lib/livebook_web/components/layout_components.ex b/lib/livebook_web/components/layout_components.ex index d488f9f21..30ad818e0 100644 --- a/lib/livebook_web/components/layout_components.ex +++ b/lib/livebook_web/components/layout_components.ex @@ -99,7 +99,7 @@ defmodule LivebookWeb.LayoutComponents do <.sidebar_link title="Home" icon="home-6-line" to={~p"/"} current={@current_page} /> <.sidebar_link - title="Apps" + title="Local apps" icon="rocket-line" to={~p"/apps-dashboard"} current={@current_page} diff --git a/lib/livebook_web/live/apps_dashboard_live.ex b/lib/livebook_web/live/apps_dashboard_live.ex index 638244621..1785552e1 100644 --- a/lib/livebook_web/live/apps_dashboard_live.ex +++ b/lib/livebook_web/live/apps_dashboard_live.ex @@ -26,15 +26,18 @@ defmodule LivebookWeb.AppsDashboardLive do current_user={@current_user} saved_hubs={@saved_hubs} > -
+
- + <.link navigate={~p"/apps"} class="flex items-center text-blue-600"> Listing <.remix_icon icon="arrow-right-line" class="align-middle ml-1" />
-
+

+ An overview of all deployed applications and previews running on this instance. +

+
<.app_list apps={@apps} />
@@ -46,7 +49,7 @@ defmodule LivebookWeb.AppsDashboardLive do ~H""" <.no_entries> You do not have any apps running.
- You can deploy new apps by opening a notebook and clicking + You can preview and deploy new apps by opening a notebook and clicking <.remix_icon icon="rocket-line" class="align-top text-lg" /> in the sidebar. """ @@ -70,10 +73,7 @@ defmodule LivebookWeb.AppsDashboardLive do
-
- App Info -
-
+
<.labeled_text label="Name"> <%= app.notebook_name %> @@ -196,7 +196,10 @@ defmodule LivebookWeb.AppsDashboardLive do
<%= col[:label] %>
@@ -205,7 +208,10 @@ defmodule LivebookWeb.AppsDashboardLive do
<%= render_slot(col, row) %>
diff --git a/lib/livebook_web/live/session_live/app_docker_component.ex b/lib/livebook_web/live/session_live/app_docker_component.ex index ff2d9a948..b4451d676 100644 --- a/lib/livebook_web/live/session_live/app_docker_component.ex +++ b/lib/livebook_web/live/session_live/app_docker_component.ex @@ -59,7 +59,7 @@ defmodule LivebookWeb.SessionLive.AppDockerComponent do ~H"""

- App deployment + App deployment with Docker

<.content file={@file} @@ -149,7 +149,7 @@ defmodule LivebookWeb.SessionLive.AppDockerComponent do <% end %> <% end %>
-
+
<.message_box :for={warning <- @warnings} kind={:warning}> <%= raw(warning) %> diff --git a/lib/livebook_web/live/session_live/app_info_component.ex b/lib/livebook_web/live/session_live/app_info_component.ex index 71b938d8b..1df6c267d 100644 --- a/lib/livebook_web/live/session_live/app_info_component.ex +++ b/lib/livebook_web/live/session_live/app_info_component.ex @@ -27,140 +27,188 @@ defmodule LivebookWeb.SessionLive.AppInfoComponent do
<% else %> -
+
<.message_box :if={@any_session_secrets?} kind={:warning} message="The notebook uses session secrets, but those are not available to deployed apps. Convert them to Hub secrets instead." /> -
-
- <.button - phx-click="deploy_app" - disabled={not Livebook.Notebook.AppSettings.valid?(@settings)} - > - <.remix_icon icon="rocket-line" /> - Deploy - - <.button color="gray" outlined patch={~p"/sessions/#{@session.id}/settings/app"}> - Configure - + +
+ <.labeled_text label="Slug" one_line> + <%= @settings.slug || "?" %> + + + <.labeled_text label="Session type" one_line> + <%= if @settings.multi_session, do: "Multi", else: "Single" %> + + + <.labeled_text label="Access" one_line> + <%= if @settings.access_type == :public do %> + No password <.remix_icon icon="lock-unlock-line" /> + <% else %> + Password protected <.remix_icon icon="lock-password-line" /> + <% end %> + +
+ + <.button color="gray" outlined patch={~p"/sessions/#{@session.id}/settings/app"}> + Configure + +
+ +

+ Remote deployment +

+ +
+ <%!-- TODO: Livebook Teams flow --%> + <.button color="blue" patch={~p"/sessions/#{@session.id}/app-docker"}> + <.remix_icon icon="rocket-line" /> Deploy with Livebook Teams + + + <.button color="gray" outlined patch={~p"/sessions/#{@session.id}/app-docker"}> + <.remix_icon icon="ship-line" /> Manual Docker deployment + +
+ +

+ Local preview +

+ +
+
+ <.labeled_text label="URL" one_line> + + <%= ~p"/apps/#{@app.slug}" %> + + + + <.labeled_text :if={@app.multi_session} label="Latest version" one_line> + v<%= @app.version %> + + +
+ Running sessions + +
+ <.app_sessions app={@app} myself={@myself} /> +
- <.link - class="text-sm text-gray-700 hover:text-blue-600" - patch={~p"/sessions/#{@session.id}/app-docker"} +
+ +
+ - <.remix_icon icon="arrow-right-line" /> - Deploy with Docker - + <%= if @app do %> + <.button + color="gray" + outlined + phx-click="deploy_app" + disabled={not Livebook.Notebook.AppSettings.valid?(@settings)} + > + <.remix_icon icon="slideshow-4-line" /> Relaunch + + <% else %> + <.button + color="blue" + phx-click="deploy_app" + disabled={not Livebook.Notebook.AppSettings.valid?(@settings)} + > + <.remix_icon icon="slideshow-4-line" /> Launch preview + + <% end %> + + <.button + :if={@app} + color="red" + outlined + type="button" + phx-click="terminate_app" + phx-target={@myself} + > + Terminate +
- <%= if @app do %> -

- Latest deployment -

-
-
- <.labeled_text label="URL" one_line> - - <%= ~p"/apps/#{@app.slug}" %> - - -
- <.labeled_text label="Session type" one_line class="grow"> - <%= if(@app.multi_session, do: "Multi", else: "Single") %> - - <.labeled_text label="Version" one_line class="grow"> - v<%= @app.version %> - -
-
-
-
- - <.icon_button - aria-label="terminate app" - phx-click={JS.push("terminate_app", target: @myself)} - > - <.remix_icon icon="delete-bin-6-line" /> - - -
-
-

- Running sessions -

-
-
-
- <.labeled_text label="Status" class="grow"> - - <.app_status status={app_session.app_status} /> - - - <.labeled_text label="Version" class="grow"> - v<%= app_session.version %> - -
-
- - <.icon_button - disabled={app_session.app_status.lifecycle} - aria-label="open app" - href={~p"/apps/#{@app.slug}/#{app_session.id}"} - > - <.remix_icon icon="link" /> - - -
- - <.icon_button aria-label="debug app" href={~p"/sessions/#{app_session.id}"}> - <.remix_icon icon="terminal-line" /> - - - <%= if app_session.app_status.lifecycle == :active do %> - - <.icon_button - aria-label="deactivate app session" - phx-click={ - JS.push("deactivate_app_session", - value: %{session_id: app_session.id}, - target: @myself - ) - } - > - <.remix_icon icon="stop-circle-line" /> - - - <% else %> - - <.icon_button - aria-label="terminate app session" - phx-click={ - JS.push("terminate_app_session", - value: %{session_id: app_session.id}, - target: @myself - ) - } - > - <.remix_icon icon="delete-bin-6-line" /> - - - <% end %> -
-
-
- <% end %> <% end %>
""" end + defp app_sessions(assigns) do + ~H""" +
+
+ <.labeled_text label="Status" class="grow"> + + <.app_status status={app_session.app_status} /> + + + <.labeled_text label="Version" class="grow"> + v<%= app_session.version %> + +
+
+
+ + <.icon_button + disabled={app_session.app_status.lifecycle != :active} + aria-label="open app" + href={~p"/apps/#{@app.slug}/#{app_session.id}"} + > + <.remix_icon icon="link" /> + + + + <.icon_button aria-label="debug app" href={~p"/sessions/#{app_session.id}"}> + <.remix_icon icon="terminal-line" /> + + + <%= if app_session.app_status.lifecycle == :active do %> + + <.icon_button + aria-label="deactivate app session" + phx-click={ + JS.push("deactivate_app_session", + value: %{session_id: app_session.id}, + target: @myself + ) + } + > + <.remix_icon icon="stop-circle-line" /> + + + <% else %> + + <.icon_button + aria-label="terminate app session" + phx-click={ + JS.push("terminate_app_session", + value: %{session_id: app_session.id}, + target: @myself + ) + } + > + <.remix_icon icon="delete-bin-6-line" /> + + + <% end %> +
+
+ """ + end + defp app_info_icon(assigns) do ~H""" session_id}, socket) do diff --git a/lib/livebook_web/live/session_live/app_settings_component.ex b/lib/livebook_web/live/session_live/app_settings_component.ex index 6a45379ac..e7d3186af 100644 --- a/lib/livebook_web/live/session_live/app_settings_component.ex +++ b/lib/livebook_web/live/session_live/app_settings_component.ex @@ -24,7 +24,14 @@ defmodule LivebookWeb.SessionLive.AppSettingsComponent do

App settings

- <.form :let={f} for={@changeset} phx-change="validate" phx-target={@myself} autocomplete="off"> + <.form + :let={f} + for={@changeset} + phx-change="validate" + phx-submit="save" + phx-target={@myself} + autocomplete="off" + >
<.text_field field={f[:slug]} label="Slug" spellcheck="false" phx-debounce />
@@ -118,13 +125,8 @@ defmodule LivebookWeb.SessionLive.AppSettingsComponent do <% end %>
- <.button - type="button" - phx-click={JS.patch(~p"/sessions/#{@session.id}") |> JS.push("deploy_app")} - disabled={not @changeset.valid?} - > - <.remix_icon icon="rocket-line" /> - Deploy + <.button type="submit" disabled={not @changeset.valid?}> + Save <.button color="gray" outlined type="reset" name="reset"> Reset @@ -148,10 +150,14 @@ defmodule LivebookWeb.SessionLive.AppSettingsComponent do |> AppSettings.change(params) |> Map.put(:action, :validate) + {:noreply, assign(socket, changeset: changeset)} + end + + def handle_event("save", %{"app_settings" => params}, socket) do with {:ok, settings} <- AppSettings.update(socket.assigns.settings, params) do Livebook.Session.set_app_settings(socket.assigns.session.pid, settings) end - {:noreply, assign(socket, changeset: changeset)} + {:noreply, push_patch(socket, to: ~p"/sessions/#{socket.assigns.session.id}")} end end 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 b6c72557e..71f04a13e 100644 --- a/lib/livebook_web/live/session_live/elixir_standalone_live.ex +++ b/lib/livebook_web/live/session_live/elixir_standalone_live.ex @@ -30,7 +30,7 @@ defmodule LivebookWeb.SessionLive.ElixirStandaloneLive do <%= @error_message %>

- Start a new local node to handle code evaluation. + Start a new local node to evaluate code.

<.button phx-click="init"> <%= if(matching_runtime?(@current_runtime), do: "Reconnect", else: "Connect") %> diff --git a/lib/livebook_web/live/session_live/render.ex b/lib/livebook_web/live/session_live/render.ex index fe5cc838e..ac3224029 100644 --- a/lib/livebook_web/live/session_live/render.ex +++ b/lib/livebook_web/live/session_live/render.ex @@ -323,20 +323,11 @@ defmodule LivebookWeb.SessionLive.Render do button_attrs={["data-el-files-list-toggle": true]} /> -
- <.button_item - icon="rocket-line" - label="App settings (sa)" - button_attrs={["data-el-app-info-toggle": true]} - /> -
-
+ <.button_item + icon="rocket-line" + label="App settings (sa)" + button_attrs={["data-el-app-info-toggle": true]} + />
@@ -373,17 +364,6 @@ defmodule LivebookWeb.SessionLive.Render do """ end - defp app_status(%{sessions: [app_session | _]}), do: app_session.app_status - defp app_status(_), do: nil - - defp app_status_color(nil), do: "bg-gray-400" - defp app_status_color(%{lifecycle: :shutting_down}), do: "bg-gray-500" - defp app_status_color(%{lifecycle: :deactivated}), do: "bg-gray-500" - defp app_status_color(%{execution: :executing}), do: "bg-blue-500" - defp app_status_color(%{execution: :executed}), do: "bg-green-bright-400" - defp app_status_color(%{execution: :error}), do: "bg-red-400" - defp app_status_color(%{execution: :interrupted}), do: "bg-gray-400" - def side_panel(assigns) do ~H"""
Runtime - <.icon_button patch={~p"/sessions/#{@session.id}/settings/runtime"}> - <.remix_icon icon="settings-3-line" /> - + + <.icon_button> + <.remix_icon icon="question-line" /> + +
@@ -615,37 +605,47 @@ defmodule LivebookWeb.SessionLive.Render do <%= value %>
-
+
<%= if Runtime.connected?(@data_view.runtime) do %> <.button phx-click="reconnect_runtime"> <.remix_icon icon="wireless-charging-line" /> Reconnect - <.button color="red" outlined type="button" phx-click="disconnect_runtime"> - Disconnect - <% else %> <.button phx-click="connect_runtime"> <.remix_icon icon="wireless-charging-line" /> Connect - <.button color="gray" outlined patch={~p"/sessions/#{@session.id}/settings/runtime"}> - Configure - <% end %> + <.button color="gray" outlined patch={~p"/sessions/#{@session.id}/settings/runtime"}> + Configure + +
+ +
+ <%= if uses_memory?(@session.memory_usage) do %> + <.memory_info memory_usage={@session.memory_usage} /> + <% else %> +
+ Memory +

+ <%= format_bytes(@session.memory_usage.system.free) %> available out of <%= format_bytes( + @session.memory_usage.system.total + ) %> +

+
+ <% end %> + + <.button + :if={Runtime.connected?(@data_view.runtime)} + color="red" + outlined + type="button" + phx-click="disconnect_runtime" + > + Disconnect +
- <%= if uses_memory?(@session.memory_usage) do %> - <.memory_info memory_usage={@session.memory_usage} /> - <% else %> -
- Memory -

- <%= format_bytes(@session.memory_usage.system.free) %> available out of <%= format_bytes( - @session.memory_usage.system.total - ) %> -

-
- <% end %>
""" @@ -655,7 +655,7 @@ defmodule LivebookWeb.SessionLive.Render do assigns = assign(assigns, :runtime_memory, runtime_memory(assigns.memory_usage)) ~H""" -
+
Memory diff --git a/test/livebook_web/live/session_live_test.exs b/test/livebook_web/live/session_live_test.exs index 09bcfdf20..2ecbb2f55 100644 --- a/test/livebook_web/live/session_live_test.exs +++ b/test/livebook_web/live/session_live_test.exs @@ -2127,7 +2127,11 @@ defmodule LivebookWeb.SessionLiveTest do |> render_change(%{"app_settings" => %{"slug" => slug}}) view - |> element(~s/#app-settings-modal button/, "Deploy") + |> element(~s/#app-settings-modal form/) + |> render_submit(%{"app_settings" => %{"slug" => slug}}) + + view + |> element(~s/[data-el-app-info] button/, "Launch preview") |> render_click() assert_receive {:app_created, %{slug: ^slug} = app}