defmodule LivebookWeb.SessionLive.CellComponent do use LivebookWeb, :live_component @impl true def render(assigns) do ~H"""
<%= render_cell(assigns) %>
""" end defp render_cell(%{cell_view: %{type: :markdown}} = assigns) do ~H""" <.cell_actions> <:secondary> <.enable_insert_mode_button /> <.insert_image_button cell_id={@cell_view.id} session_id={@session_id} socket={@socket} /> <.cell_link_button cell_id={@cell_view.id} /> <.move_cell_up_button cell_id={@cell_view.id} /> <.move_cell_down_button cell_id={@cell_view.id} /> <.delete_cell_button cell_id={@cell_view.id} /> <.cell_body>
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent} id={"#{@cell_view.id}-primary"} cell_id={@cell_view.id} tag="primary" source_view={@cell_view.source_view} language="markdown" />
<.content_skeleton empty={empty?(@cell_view.source_view)} />
""" end defp render_cell(%{cell_view: %{type: :code}} = assigns) do ~H""" <.cell_actions> <:primary> <.cell_evaluation_button session_id={@session_id} socket={@socket} cell_id={@cell_view.id} validity={@cell_view.eval.validity} status={@cell_view.eval.status} reevaluate_automatically={@cell_view.reevaluate_automatically} /> <:secondary> <.amplify_output_button /> <.cell_settings_button cell_id={@cell_view.id} socket={@socket} session_id={@session_id} /> <.cell_link_button cell_id={@cell_view.id} /> <.move_cell_up_button cell_id={@cell_view.id} /> <.move_cell_down_button cell_id={@cell_view.id} /> <.delete_cell_button cell_id={@cell_view.id} /> <.cell_body>
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent} id={"#{@cell_view.id}-primary"} cell_id={@cell_view.id} tag="primary" source_view={@cell_view.source_view} language="elixir" intellisense />
<.cell_status id={@cell_view.id} cell_view={@cell_view} />
<.evaluation_outputs cell_view={@cell_view} socket={@socket} session_id={@session_id} runtime={@runtime} /> """ end defp render_cell(%{cell_view: %{type: :setup}} = assigns) do ~H""" <.cell_actions> <:primary> <.setup_cell_evaluation_button cell_id={@cell_view.id} validity={@cell_view.eval.validity} status={@cell_view.eval.status} /> <:secondary> <.enable_insert_mode_button /> <.dependency_search_button session_id={@session_id} runtime={@runtime} socket={@socket} /> <.cell_link_button cell_id={@cell_view.id} /> <.setup_cell_info /> <.cell_body>
Notebook dependencies and setup <.cell_status id={"#{@cell_view.id}-1"} cell_view={@cell_view} />
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent} id={"#{@cell_view.id}-primary"} cell_id={@cell_view.id} tag="primary" source_view={@cell_view.source_view} language="elixir" intellisense />
<.cell_status id={"#{@cell_view.id}-2"} cell_view={@cell_view} />
<.evaluation_outputs cell_view={@cell_view} socket={@socket} session_id={@session_id} runtime={@runtime} />
""" end defp render_cell(%{cell_view: %{type: :smart}} = assigns) do ~H""" <.cell_actions> <:primary> <.cell_evaluation_button session_id={@session_id} socket={@socket} cell_id={@cell_view.id} validity={@cell_view.eval.validity} status={@cell_view.eval.status} reevaluate_automatically={false} /> <:secondary> <.toggle_source_button /> <.convert_smart_cell_button cell_id={@cell_view.id} /> <.cell_link_button cell_id={@cell_view.id} /> <.move_cell_up_button cell_id={@cell_view.id} /> <.move_cell_down_button cell_id={@cell_view.id} /> <.delete_cell_button cell_id={@cell_view.id} /> <.cell_body>
<%= case @cell_view.status do %> <% :started -> %>
<.live_component module={LivebookWeb.JSViewComponent} id={@cell_view.id} js_view={@cell_view.js_view} session_id={@session_id} /> <%= if @cell_view.editor do %> <.live_component module={LivebookWeb.SessionLive.CellEditorComponent} id={"#{@cell_view.id}-secondary"} cell_id={@cell_view.id} tag="secondary" source_view={@cell_view.editor.source_view} language={@cell_view.editor.language} /> <% end %>
<% :dead -> %>
Evaluate and install dependencies to show the contents of this Smart cell.
<% :starting -> %>
<.content_skeleton empty={false} />
<% end %>
<.cell_status id={"#{@cell_view.id}-1"} cell_view={@cell_view} />
<.live_component module={LivebookWeb.SessionLive.CellEditorComponent} id={"#{@cell_view.id}-primary"} cell_id={@cell_view.id} tag="primary" source_view={@cell_view.source_view} language="elixir" intellisense read_only />
<.cell_status id={"#{@cell_view.id}-2"} cell_view={@cell_view} />
<.evaluation_outputs cell_view={@cell_view} socket={@socket} session_id={@session_id} runtime={@runtime} /> """ end defp cell_actions(assigns) do assigns = assigns |> assign_new(:primary, fn -> [] end) |> assign_new(:secondary, fn -> [] end) ~H"""
<%= render_slot(@primary) %>
""" end defp cell_body(assigns) do ~H"""
<%= render_slot(@inner_block) %>
""" end defp cell_evaluation_button(%{status: :ready, reevaluate_automatically: true} = assigns) when assigns.validity in [:evaluated, :stale] do ~H""" <%= live_patch to: Routes.session_path(@socket, :cell_settings, @session_id, @cell_id), class: "text-gray-600 hover:text-gray-800 focus:text-gray-800 flex space-x-1 items-center" do %> <.remix_icon icon="check-line" class="text-xl" /> Reevaluates automatically <% end %> """ end defp cell_evaluation_button(%{status: :ready} = assigns) do ~H""" """ end defp cell_evaluation_button(assigns) do ~H""" """ end defp setup_cell_evaluation_button(%{status: :ready} = assigns) do ~H""" """ end defp setup_cell_evaluation_button(assigns) do ~H""" """ end defp enable_insert_mode_button(assigns) do ~H""" """ end defp insert_image_button(assigns) do ~H""" <%= live_patch to: Routes.session_path(@socket, :cell_upload, @session_id, @cell_id), class: "icon-button", aria_label: "insert image", role: "button" do %> <.remix_icon icon="image-add-line" class="text-xl" /> <% end %> """ end defp toggle_source_button(assigns) do ~H""" """ end defp convert_smart_cell_button(assigns) do ~H""" """ end defp dependency_search_button(assigns) do ~H""" <%= if Livebook.Runtime.fixed_dependencies?(@runtime) do %> <% else %> <%= live_patch to: Routes.session_path(@socket, :dependency_search, @session_id), class: "icon-button", aria_label: "add dependency", role: "button", data_btn_dependency_search: true do %> <.remix_icon icon="play-list-add-line" class="text-xl" /> <% end %> <% end %> """ end defp cell_link_button(assigns) do ~H""" <.remix_icon icon="link" class="text-xl" /> """ end def amplify_output_button(assigns) do ~H""" """ end defp cell_settings_button(assigns) do ~H""" <%= live_patch to: Routes.session_path(@socket, :cell_settings, @session_id, @cell_id), class: "icon-button", aria_label: "cell settings", role: "button" do %> <.remix_icon icon="settings-3-line" class="text-xl" /> <% end %> """ end defp move_cell_up_button(assigns) do ~H""" """ end defp move_cell_down_button(assigns) do ~H""" """ end defp delete_cell_button(assigns) do ~H""" """ end defp setup_cell_info(assigns) do ~H""" <.remix_icon icon="question-line" class="text-xl" /> """ end defp evaluation_outputs(assigns) do ~H"""
""" end defp empty?(%{source: ""} = _source_view), do: true defp empty?(_source_view), do: false defp cell_status(%{cell_view: %{eval: %{status: :evaluating}}} = assigns) do ~H""" <.status_indicator circle_class="bg-blue-500" animated_circle_class="bg-blue-400" change_indicator={true}> """ end defp cell_status(%{cell_view: %{eval: %{status: :queued}}} = assigns) do ~H""" <.status_indicator circle_class="bg-gray-400" animated_circle_class="bg-gray-300"> Queued """ end defp cell_status(%{cell_view: %{eval: %{validity: :evaluated}}} = assigns) do ~H""" <.status_indicator circle_class="bg-green-bright-400" change_indicator={true} tooltip={evaluated_label(@cell_view.eval.evaluation_time_ms)}> Evaluated """ end defp cell_status(%{cell_view: %{eval: %{validity: :stale}}} = assigns) do ~H""" <.status_indicator circle_class="bg-yellow-bright-200" change_indicator={true}> Stale """ end defp cell_status(%{cell_view: %{eval: %{validity: :aborted}}} = assigns) do ~H""" <.status_indicator circle_class="bg-gray-500"> Aborted """ end defp cell_status(assigns), do: ~H"" defp status_indicator(assigns) do assigns = assigns |> assign_new(:animated_circle_class, fn -> nil end) |> assign_new(:change_indicator, fn -> false end) |> assign_new(:tooltip, fn -> nil end) ~H"""
<%= render_slot(@inner_block) %> <%= if @change_indicator do %> * <% end %>
<%= if @animated_circle_class do %> <% end %>
""" end defp evaluated_label(time_ms) when is_integer(time_ms) do evaluation_time = if time_ms > 100 do seconds = time_ms |> Kernel./(1000) |> Float.floor(1) "#{seconds}s" else "#{time_ms}ms" end "Took " <> evaluation_time end defp evaluated_label(_time_ms), do: nil end