Prepare to make Explore section extensible (#626)

This commit is contained in:
José Valim 2021-10-20 22:27:00 +02:00 committed by GitHub
parent 3ab2a56924
commit b930d8620a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 107 deletions

View file

@ -1,50 +1,3 @@
defmodule Livebook.Notebook.Explore.Utils do
@moduledoc false
@doc """
Defines a module attribute `attr` with notebook info.
"""
defmacro defnotebook(attr, props) do
quote bind_quoted: [attr: attr, props: props] do
{path, notebook_info} = Livebook.Notebook.Explore.Utils.fetch_notebook!(attr, props)
@external_resource path
Module.put_attribute(__MODULE__, attr, notebook_info)
end
end
def fetch_notebook!(attr, props) do
name = Atom.to_string(attr)
path = Path.join([__DIR__, "explore", name <> ".livemd"])
markdown = File.read!(path)
# Parse the file to ensure no warnings and read the title.
# However, in the info we keep just the file contents to save on memory.
{notebook, []} = Livebook.LiveMarkdown.Import.notebook_from_markdown(markdown)
images =
props
|> Keyword.get(:image_names, [])
|> Map.new(fn image_name ->
path = Path.join([__DIR__, "explore", "images", image_name])
content = File.read!(path)
{image_name, content}
end)
notebook_info = %{
slug: String.replace(name, "_", "-"),
livemd: markdown,
title: notebook.name,
description: Keyword.fetch!(props, :description),
image_url: Keyword.fetch!(props, :image_url),
images: images
}
{path, notebook_info}
end
end
defmodule Livebook.Notebook.Explore do
@moduledoc false
@ -58,78 +11,99 @@ defmodule Livebook.Notebook.Explore do
end
end
import Livebook.Notebook.Explore.Utils
defnotebook(:intro_to_livebook,
description: "Get to know Livebook, see how it works and explore its features.",
image_url: "/images/logo.png"
)
defnotebook(:distributed_portals_with_elixir,
description:
"A fast-paced introduction to the Elixir language by building distributed data-transfer portals.",
image_url: "/images/elixir-portal.jpeg",
image_names: ["portal-drop.jpeg", "portal-list.jpeg"]
)
defnotebook(:elixir_and_livebook,
description: "Learn how to use some of Elixir and Livebook's unique features together.",
image_url: "/images/elixir.png"
)
defnotebook(:intro_to_nx,
description:
"Enter Numerical Elixir, experience the power of multi-dimensional arrays of numbers.",
image_url: "/images/nx.png"
)
defnotebook(:intro_to_axon,
description: "Build Neural Networks in Elixir using a high-level, composable API.",
image_url: "/images/axon.png"
)
defnotebook(:intro_to_vega_lite,
description: "Learn how to quickly create numerous plots for your data.",
image_url: "/images/vega_lite.png"
)
defnotebook(:vm_introspection,
description: "Extract and visualize information about a remote running node.",
image_url: "/images/vm_introspection.png"
)
defnotebook(:intro_to_kino,
description: "Display and control rich and interactive widgets in Livebook.",
image_url: "/images/kino.png"
)
@type notebook_info :: %{
slug: String.t(),
livemd: String.t(),
title: String.t(),
description: String.t(),
image_url: String.t(),
cover_url: String.t(),
images: images()
}
@type images :: %{String.t() => binary()}
infos = [
%{
path: Path.join(__DIR__, "explore/intro_to_livebook.livemd"),
description: "Get to know Livebook, see how it works and explore its features.",
cover_url: "/images/logo.png"
},
%{
path: Path.join(__DIR__, "explore/distributed_portals_with_elixir.livemd"),
description:
"A fast-paced introduction to the Elixir language by building distributed data-transfer portals.",
cover_url: "/images/elixir-portal.jpeg",
image_names: ["portal-drop.jpeg", "portal-list.jpeg"]
},
%{
path: Path.join(__DIR__, "explore/elixir_and_livebook.livemd"),
description: "Learn how to use some of Elixir and Livebook's unique features together.",
cover_url: "/images/elixir.png"
},
%{
path: Path.join(__DIR__, "explore/intro_to_vega_lite.livemd"),
description: "Learn how to quickly create numerous plots for your data.",
cover_url: "/images/vega_lite.png"
},
%{
path: Path.join(__DIR__, "explore/intro_to_kino.livemd"),
description: "Display and control rich and interactive widgets in Livebook.",
cover_url: "/images/kino.png"
},
%{
path: Path.join(__DIR__, "explore/intro_to_nx.livemd"),
description:
"Enter Numerical Elixir, experience the power of multi-dimensional arrays of numbers.",
cover_url: "/images/nx.png"
},
# %{
# path: Path.join(__DIR__, "explore/intro_to_axon.livemd"),
# description: "Build Neural Networks in Elixir using a high-level, composable API.",
# cover_url: "/images/axon.png"
# },
%{
path: Path.join(__DIR__, "explore/vm_introspection.livemd"),
description: "Extract and visualize information about a remote running node.",
cover_url: "/images/vm_introspection.png"
}
]
notebook_infos =
for info <- infos do
path = Map.fetch!(info, :path)
@external_resource path
markdown = File.read!(path)
# Parse the file to ensure no warnings and read the title.
# However, in the info we keep just the file contents to save on memory.
{notebook, []} = Livebook.LiveMarkdown.Import.notebook_from_markdown(markdown)
images =
info
|> Map.get(:image_names, [])
|> Map.new(fn image_name ->
path = Path.join([Path.dirname(path), "images", image_name])
content = File.read!(path)
{image_name, content}
end)
slug = info[:slug] || path |> Path.basename() |> Path.rootname() |> String.replace("_", "-")
%{
slug: slug,
livemd: markdown,
title: notebook.name,
description: Map.fetch!(info, :description),
cover_url: Map.fetch!(info, :cover_url),
images: images
}
end
@doc """
Returns a list of example notebooks with metadata.
"""
@spec notebook_infos() :: list(notebook_info())
def notebook_infos() do
[
@intro_to_livebook,
@distributed_portals_with_elixir,
@elixir_and_livebook,
@intro_to_vega_lite,
@intro_to_kino,
@intro_to_nx,
@vm_introspection
# , @intro_to_axon
]
end
def notebook_infos(), do: unquote(Macro.escape(notebook_infos))
@doc """
Finds explore notebook by slug and returns the parsed data structure.

View file

@ -11,7 +11,7 @@ defmodule LivebookWeb.ExploreHelpers do
<div class="flex flex-col">
<%= live_redirect to: Routes.explore_path(@socket, :notebook, @notebook_info.slug),
class: "flex items-center justify-center p-6 border-2 border-gray-100 rounded-t-2xl h-[150px]" do %>
<img src={@notebook_info.image_url} class="max-h-full max-w-[75%]" />
<img src={@notebook_info.cover_url} class="max-h-full max-w-[75%]" />
<% end %>
<div class="px-6 py-4 bg-gray-100 rounded-b-2xl flex-grow">
<%= live_redirect @notebook_info.title,

View file

@ -63,7 +63,7 @@ defmodule LivebookWeb.ExploreLive do
</div>
</div>
<div class="flex-grow hidden md:flex flex items-center justify-center">
<img src={@lead_notebook_info.image_url} height="120" width="120" alt="livebook" />
<img src={@lead_notebook_info.cover_url} height="120" width="120" alt="livebook" />
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">