defmodule LivebookWeb.Output.StdoutComponent do use LivebookWeb, :live_component @impl true def mount(socket) do {:ok, assign(socket, modifiers: [], last_line: nil, last_html_line: nil), temporary_assigns: [html_lines: []]} end @impl true def update(assigns, socket) do {text, assigns} = Map.pop(assigns, :text) socket = assign(socket, assigns) if text do text = (socket.assigns.last_line || "") <> text # Captured output usually has a trailing newline that we ignore # for HTML conversion, since each line is an HTML block anyway trailing_newline? = String.ends_with?(text, "\n") text = String.replace_suffix(text, "\n", "") text = Livebook.Utils.apply_rewind(text) last_line = case Livebook.Utils.split_at_last_occurrence(text, "\n") do :error -> text {:ok, _, last_line} -> last_line end last_line = last_line <> if(trailing_newline?, do: "\n", else: "") {html_lines, modifiers} = LivebookWeb.Helpers.ANSI.ansi_string_to_html_lines_step(text, socket.assigns.modifiers) {html_lines, [last_html_line]} = Enum.split(html_lines, -1) {:ok, assign(socket, html_lines: html_lines, last_html_line: last_html_line, last_line: last_line, modifiers: modifiers )} else {:ok, socket} end end @impl true def render(assigns) do ~H"""
<%# Note 1: We add a newline to each element, so that multiple lines can be copied properly as element.textContent %> <%# Note 2: We use comments to avoid inserting unintended whitespace %>
""" end end