Packages
livebook
0.5.0
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook_web/live/output/stdout_component.ex
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"""
<div id={"virtualized-text-#{@id}"}
class="relative"
phx-hook="VirtualizedLines"
data-max-height="300"
data-follow={to_string(@follow)}>
<%# 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 %>
<div data-template class="hidden" id={"virtualized-text-#{@id}-template"}><%#
%><div id={"virtualized-text-#{@id}-template-append"} phx-update="append"><%#
%><%= for html_line <- @html_lines do %><%#
%><div data-line id={Livebook.Utils.random_id()}><%= [html_line, "\n"] %></div><%#
%><% end %><%#
%></div><%#
%><div data-line><%= @last_html_line %></div><%#
%></div>
<div data-content class="overflow-auto whitespace-pre font-editor text-gray-500 tiny-scrollbar"
id={"virtualized-text-#{@id}-content"}
phx-update="ignore"></div>
<div class="absolute right-2 top-0 z-10">
<button class="icon-button bg-gray-100"
data-element="clipcopy"
phx-click={JS.dispatch("lb:clipcopy", to: "#virtualized-text-#{@id}-template")}>
<.remix_icon icon="clipboard-line" class="text-lg" />
</button>
</div>
</div>
"""
end
end