mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-08 12:36:36 +08:00
Make frame and text stream updates implicit (#2451)
This commit is contained in:
parent
8431401df1
commit
9bc0832e03
6 changed files with 98 additions and 62 deletions
|
@ -410,11 +410,11 @@ defmodule LivebookWeb.AppSessionLive do
|
|||
changed_input_ids = Session.Data.changed_input_ids(data)
|
||||
|
||||
for {{idx, frame}, cell} <- Notebook.find_frame_outputs(data.notebook, ref) do
|
||||
input_views = input_views_for_cell(cell, data, changed_input_ids)
|
||||
|
||||
send_update(LivebookWeb.Output.FrameComponent,
|
||||
id: "outputs-#{idx}-output",
|
||||
outputs: frame.outputs,
|
||||
update_type: update_type,
|
||||
input_views: input_views_for_cell(cell, data, changed_input_ids)
|
||||
event: {:update, update_type, frame.outputs, input_views}
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -432,7 +432,7 @@ defmodule LivebookWeb.AppSessionLive do
|
|||
:markdown -> LivebookWeb.Output.MarkdownComponent
|
||||
end
|
||||
|
||||
send_update(module, id: "outputs-#{idx}-output", text: output.text)
|
||||
send_update(module, id: "outputs-#{idx}-output", event: {:append, output.text})
|
||||
data_view
|
||||
|
||||
_ ->
|
||||
|
|
|
@ -7,19 +7,11 @@ defmodule LivebookWeb.Output.FrameComponent do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def update(assigns, socket) do
|
||||
{update_type, assigns} = Map.pop(assigns, :update_type, nil)
|
||||
{outputs, assigns} = Map.pop!(assigns, :outputs)
|
||||
|
||||
socket = assign(socket, assigns)
|
||||
|
||||
socket = assign_new(socket, :num_outputs, fn -> length(outputs) end)
|
||||
def update(%{event: {:update, update_type, outputs, input_views}}, socket) do
|
||||
socket = assign(socket, input_views: input_views)
|
||||
|
||||
socket =
|
||||
case update_type do
|
||||
nil ->
|
||||
stream(socket, :outputs, stream_items(outputs))
|
||||
|
||||
:replace ->
|
||||
socket
|
||||
|> assign(num_outputs: length(outputs))
|
||||
|
@ -34,6 +26,17 @@ defmodule LivebookWeb.Output.FrameComponent do
|
|||
{:ok, socket}
|
||||
end
|
||||
|
||||
def update(assigns, socket) do
|
||||
{outputs, assigns} = Map.pop!(assigns, :outputs)
|
||||
|
||||
socket = assign(socket, assigns)
|
||||
|
||||
socket = assign_new(socket, :num_outputs, fn -> length(outputs) end)
|
||||
socket = stream(socket, :outputs, stream_items(outputs))
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
defp stream_items(outputs) do
|
||||
for {idx, output} <- Enum.reverse(outputs) do
|
||||
%{id: Integer.to_string(idx), idx: idx, output: output}
|
||||
|
|
|
@ -5,24 +5,34 @@ defmodule LivebookWeb.Output.MarkdownComponent do
|
|||
def mount(socket) do
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(allowed_uri_schemes: Livebook.Config.allowed_uri_schemes())
|
||||
|> assign(allowed_uri_schemes: Livebook.Config.allowed_uri_schemes(), initialized: false)
|
||||
|> stream(:chunks, [])}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(%{event: {:append, text}}, socket) do
|
||||
{:ok, append_text(socket, text)}
|
||||
end
|
||||
|
||||
def update(assigns, socket) do
|
||||
{text, assigns} = Map.pop(assigns, :text)
|
||||
|
||||
socket = assign(socket, assigns)
|
||||
|
||||
if text do
|
||||
chunk = %{id: Livebook.Utils.random_long_id(), text: text}
|
||||
{:ok, stream_insert(socket, :chunks, chunk)}
|
||||
else
|
||||
if socket.assigns.initialized do
|
||||
{:ok, socket}
|
||||
else
|
||||
{:ok,
|
||||
socket
|
||||
|> append_text(text)
|
||||
|> assign(:initialized, true)}
|
||||
end
|
||||
end
|
||||
|
||||
defp append_text(socket, text) do
|
||||
chunk = %{id: Livebook.Utils.random_long_id(), text: text}
|
||||
stream_insert(socket, :chunks, chunk)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
|
|
|
@ -3,23 +3,36 @@ defmodule LivebookWeb.Output.PlainTextComponent do
|
|||
|
||||
@impl true
|
||||
def mount(socket) do
|
||||
{:ok, stream(socket, :chunks, [])}
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(initialized: false)
|
||||
|> stream(:chunks, [])}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(%{event: {:append, text}}, socket) do
|
||||
{:ok, append_text(socket, text)}
|
||||
end
|
||||
|
||||
def update(assigns, socket) do
|
||||
{text, assigns} = Map.pop(assigns, :text)
|
||||
|
||||
socket = assign(socket, assigns)
|
||||
|
||||
if text do
|
||||
chunk = %{id: Livebook.Utils.random_long_id(), text: text}
|
||||
{:ok, stream_insert(socket, :chunks, chunk)}
|
||||
else
|
||||
if socket.assigns.initialized do
|
||||
{:ok, socket}
|
||||
else
|
||||
{:ok,
|
||||
socket
|
||||
|> append_text(text)
|
||||
|> assign(:initialized, true)}
|
||||
end
|
||||
end
|
||||
|
||||
defp append_text(socket, text) do
|
||||
chunk = %{id: Livebook.Utils.random_long_id(), text: text}
|
||||
stream_insert(socket, :chunks, chunk)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
|
|
|
@ -5,47 +5,57 @@ defmodule LivebookWeb.Output.TerminalTextComponent do
|
|||
def mount(socket) do
|
||||
{:ok,
|
||||
socket
|
||||
|> assign(modifiers: [], last_line: nil, last_html_line: nil)
|
||||
|> assign(modifiers: [], last_line: nil, last_html_line: nil, initialized: false)
|
||||
|> stream(:html_lines, [])}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def update(%{event: {:append, text}}, socket) do
|
||||
{:ok, append_text(socket, text)}
|
||||
end
|
||||
|
||||
def update(assigns, socket) do
|
||||
{text, assigns} = Map.pop(assigns, :text)
|
||||
socket = assign(socket, assigns)
|
||||
|
||||
if text do
|
||||
text = (socket.assigns.last_line || "") <> text
|
||||
|
||||
text = Livebook.Notebook.normalize_terminal_text(text)
|
||||
|
||||
last_line =
|
||||
case Livebook.Utils.split_at_last_occurrence(text, "\n") do
|
||||
:error -> text
|
||||
{:ok, _, last_line} -> last_line
|
||||
end
|
||||
|
||||
{html_lines, modifiers} =
|
||||
LivebookWeb.ANSIHelpers.ansi_string_to_html_lines_step(text, socket.assigns.modifiers)
|
||||
|
||||
{html_lines, [last_html_line]} = Enum.split(html_lines, -1)
|
||||
|
||||
stream_items =
|
||||
for html_line <- html_lines, do: %{id: Livebook.Utils.random_long_id(), html: html_line}
|
||||
|
||||
socket = stream(socket, :html_lines, stream_items)
|
||||
|
||||
{:ok,
|
||||
assign(socket,
|
||||
last_html_line: last_html_line,
|
||||
last_line: last_line,
|
||||
modifiers: modifiers
|
||||
)}
|
||||
else
|
||||
if socket.assigns.initialized do
|
||||
{:ok, socket}
|
||||
else
|
||||
{:ok,
|
||||
socket
|
||||
|> append_text(text)
|
||||
|> assign(:initialized, true)}
|
||||
end
|
||||
end
|
||||
|
||||
defp append_text(socket, text) do
|
||||
text = (socket.assigns.last_line || "") <> text
|
||||
|
||||
text = Livebook.Notebook.normalize_terminal_text(text)
|
||||
|
||||
last_line =
|
||||
case Livebook.Utils.split_at_last_occurrence(text, "\n") do
|
||||
:error -> text
|
||||
{:ok, _, last_line} -> last_line
|
||||
end
|
||||
|
||||
{html_lines, modifiers} =
|
||||
LivebookWeb.ANSIHelpers.ansi_string_to_html_lines_step(text, socket.assigns.modifiers)
|
||||
|
||||
{html_lines, [last_html_line]} = Enum.split(html_lines, -1)
|
||||
|
||||
stream_items =
|
||||
for html_line <- html_lines, do: %{id: Livebook.Utils.random_long_id(), html: html_line}
|
||||
|
||||
socket = stream(socket, :html_lines, stream_items)
|
||||
|
||||
assign(socket,
|
||||
last_html_line: last_html_line,
|
||||
last_line: last_line,
|
||||
modifiers: modifiers
|
||||
)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
|
|
|
@ -2923,14 +2923,14 @@ defmodule LivebookWeb.SessionLive do
|
|||
changed_input_ids = Session.Data.changed_input_ids(data)
|
||||
|
||||
for {{idx, frame}, cell} <- Notebook.find_frame_outputs(data.notebook, ref) do
|
||||
# Note that we are not updating data_view to avoid re-render,
|
||||
# but any change that causes frame to re-render will update
|
||||
# data_view first
|
||||
input_views = input_views_for_cell(cell, data, changed_input_ids)
|
||||
|
||||
send_update(LivebookWeb.Output.FrameComponent,
|
||||
id: "outputs-#{idx}-output",
|
||||
outputs: frame.outputs,
|
||||
update_type: update_type,
|
||||
# Note that we are not updating data_view to avoid re-render,
|
||||
# but any change that causes frame to re-render will update
|
||||
# data_view first
|
||||
input_views: input_views_for_cell(cell, data, changed_input_ids)
|
||||
event: {:update, update_type, frame.outputs, input_views}
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -2948,7 +2948,7 @@ defmodule LivebookWeb.SessionLive do
|
|||
:markdown -> LivebookWeb.Output.MarkdownComponent
|
||||
end
|
||||
|
||||
send_update(module, id: "outputs-#{idx}-output", text: output.text)
|
||||
send_update(module, id: "outputs-#{idx}-output", event: {:append, output.text})
|
||||
data_view
|
||||
|
||||
_ ->
|
||||
|
|
Loading…
Add table
Reference in a new issue