This commit is contained in:
Sergey ⚗️ Kuznetsov 2025-08-21 16:16:46 +02:00 committed by GitHub
commit afe6bad0f6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 84 additions and 10 deletions

View file

@ -14,7 +14,8 @@ defmodule Livebook.Notebook.AppSettings do
access_type: access_type(),
password: String.t() | nil,
show_source: boolean(),
output_type: output_type()
output_type: output_type(),
render_static: boolean()
}
@type access_type :: :public | :protected
@ -33,6 +34,7 @@ defmodule Livebook.Notebook.AppSettings do
field :password, :string
field :show_source, :boolean
field :output_type, Ecto.Enum, values: [:all, :rich]
field :render_static, :boolean
end
@doc """
@ -49,7 +51,8 @@ defmodule Livebook.Notebook.AppSettings do
access_type: :protected,
password: generate_password(),
show_source: false,
output_type: :all
output_type: :all,
render_static: false
}
end
@ -82,14 +85,16 @@ defmodule Livebook.Notebook.AppSettings do
:auto_shutdown_ms,
:access_type,
:show_source,
:output_type
:output_type,
:render_static
])
|> validate_required([
:slug,
:multi_session,
:access_type,
:show_source,
:output_type
:output_type,
:render_static
])
|> validate_format(:slug, ~r/^[a-z0-9-]+$/,
message: "should only contain lowercase alphanumeric characters and dashes"

View file

@ -49,6 +49,13 @@ defmodule Livebook.Notebook.Cell do
def evaluable?(%Cell.Smart{}), do: true
def evaluable?(_cell), do: false
@doc """
Checks if the given cell can be statically rendered
"""
@spec static?(t()) :: boolean()
def static?(%Cell.Markdown{}), do: true
def static?(_), do: false
@doc """
Extracts all inputs from the given indexed output.
"""

View file

@ -480,17 +480,42 @@ defmodule LivebookWeb.AppSessionLive do
defp data_to_view(data) do
changed_input_ids = Session.Data.changed_input_ids(data)
%{
notebook_name: data.notebook.name,
cell_views:
for {cell, _section} <- Notebook.evaluable_cells_with_section(data.notebook) do
shall_render = fn cell ->
if data.notebook.app_settings.render_static do
Cell.evaluable?(cell) or Cell.static?(cell)
else
Cell.evaluable?(cell)
end
end
cell_views =
data.notebook
|> Notebook.cells_with_section()
|> Enum.filter(fn {cell, _section} -> shall_render.(cell) end)
|> Enum.map(fn
{%Livebook.Notebook.Cell.Markdown{} = cell, _section} ->
out_id = :rand.uniform(31337)
output = {out_id, %{type: :markdown_static, text: cell.source, chunk: false}}
%{
id: cell.id,
input_views: [],
outputs: [output],
outputs_batch_number: 0
}
{cell, _section} ->
%{
id: cell.id,
input_views: input_views_for_cell(cell, data, changed_input_ids),
outputs: filter_outputs(cell.outputs, data.notebook.app_settings.output_type),
outputs_batch_number: data.cell_infos[cell.id].eval.outputs_batch_number
}
end,
end)
%{
notebook_name: data.notebook.name,
cell_views: cell_views,
app_status: data.app_data.status,
show_source: data.notebook.app_settings.show_source,
slug: data.notebook.app_settings.slug,

View file

@ -64,6 +64,19 @@ defmodule LivebookWeb.Output do
"""
end
defp render_output(%{type: :markdown_static} = output, %{id: id, session_id: session_id}) do
assigns = %{id: id, session_id: session_id, output: output}
~H"""
<.live_component
module={Output.MarkdownStaticComponent}
id={@id}
session_id={@session_id}
output={@output}
/>
"""
end
defp render_output(%{type: :image} = output, %{id: id}) do
assigns = %{id: id, content: output.content, mime_type: output.mime_type}

View file

@ -0,0 +1,12 @@
defmodule LivebookWeb.Output.MarkdownStaticComponent do
use LivebookWeb, :live_component
@impl true
def render(assigns) do
~H"""
<div>{to_html(@output.text)}</div>
"""
end
defp to_html(markdown), do: Earmark.as_html!(markdown) |> Phoenix.HTML.raw()
end

View file

@ -105,6 +105,17 @@ defmodule LivebookWeb.SessionLive.AppSettingsComponent do
'''
}
/>
<.checkbox_field
field={f[:render_static]}
label="Render sections and Markdown blocks"
help={
~S'''
When enabled, renders all the added
h2-sections and Markdown blocks of
the original notebook.
'''
}
/>
<%= if Ecto.Changeset.get_field(@changeset, :multi_session) do %>
<.checkbox_field
field={f[:show_existing_sessions]}

View file

@ -111,7 +111,7 @@ defmodule Livebook.MixProject do
{:bandit, "~> 1.0"},
{:plug, "~> 1.16"},
{:plug_crypto, "~> 2.0"},
{:earmark_parser, "~> 1.4"},
{:earmark, "~> 1.4"},
{:ecto, "~> 3.10"},
{:phoenix_ecto, "~> 4.4"},
{:aws_credentials, "~> 0.3.0", runtime: false},

View file

@ -10,6 +10,7 @@
"cowlib": {:hex, :cowlib, "2.13.0", "db8f7505d8332d98ef50a3ef34b34c1afddec7506e4ee4dd4a3a266285d282ca", [:make, :rebar3], [], "hexpm", "e1e1284dc3fc030a64b1ad0d8382ae7e99da46c3246b815318a4b848873800a4"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
"earmark": {:hex, :earmark, "1.4.47", "7e7596b84fe4ebeb8751e14cbaeaf4d7a0237708f2ce43630cfd9065551f94ca", [:mix], [], "hexpm", "3e96bebea2c2d95f3b346a7ff22285bc68a99fbabdad9b655aa9c6be06c698f8"},
"earmark_parser": {:hex, :earmark_parser, "1.4.43", "34b2f401fe473080e39ff2b90feb8ddfeef7639f8ee0bbf71bb41911831d77c5", [:mix], [], "hexpm", "970a3cd19503f5e8e527a190662be2cee5d98eed1ff72ed9b3d1a3d466692de8"},
"ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"},
"eini": {:hex, :eini_beam, "2.2.4", "02143b1dce4dda4243248e7d9b3d8274b8d9f5a666445e3d868e2cce79e4ff22", [:rebar3], [], "hexpm", "12de479d144b19e09bb92ba202a7ea716739929afdf9dff01ad802e2b1508471"},