mirror of
				https://github.com/livebook-dev/livebook.git
				synced 2025-10-31 15:56:05 +08:00 
			
		
		
		
	Prepare to make Explore section extensible (#626)
This commit is contained in:
		
							parent
							
								
									3ab2a56924
								
							
						
					
					
						commit
						b930d8620a
					
				
					 3 changed files with 81 additions and 107 deletions
				
			
		|  | @ -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 | defmodule Livebook.Notebook.Explore do | ||||||
|   @moduledoc false |   @moduledoc false | ||||||
| 
 | 
 | ||||||
|  | @ -58,78 +11,99 @@ defmodule Livebook.Notebook.Explore do | ||||||
|     end |     end | ||||||
|   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 :: %{ |   @type notebook_info :: %{ | ||||||
|           slug: String.t(), |           slug: String.t(), | ||||||
|           livemd: String.t(), |           livemd: String.t(), | ||||||
|           title: String.t(), |           title: String.t(), | ||||||
|           description: String.t(), |           description: String.t(), | ||||||
|           image_url: String.t(), |           cover_url: String.t(), | ||||||
|           images: images() |           images: images() | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|   @type images :: %{String.t() => binary()} |   @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 """ |   @doc """ | ||||||
|   Returns a list of example notebooks with metadata. |   Returns a list of example notebooks with metadata. | ||||||
|   """ |   """ | ||||||
|   @spec notebook_infos() :: list(notebook_info()) |   @spec notebook_infos() :: list(notebook_info()) | ||||||
|   def notebook_infos() do |   def notebook_infos(), do: unquote(Macro.escape(notebook_infos)) | ||||||
|     [ |  | ||||||
|       @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 |  | ||||||
| 
 | 
 | ||||||
|   @doc """ |   @doc """ | ||||||
|   Finds explore notebook by slug and returns the parsed data structure. |   Finds explore notebook by slug and returns the parsed data structure. | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ defmodule LivebookWeb.ExploreHelpers do | ||||||
|     <div class="flex flex-col"> |     <div class="flex flex-col"> | ||||||
|       <%= live_redirect to: Routes.explore_path(@socket, :notebook, @notebook_info.slug), |       <%= 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 %> |             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 %> |       <% end %> | ||||||
|       <div class="px-6 py-4 bg-gray-100 rounded-b-2xl flex-grow"> |       <div class="px-6 py-4 bg-gray-100 rounded-b-2xl flex-grow"> | ||||||
|         <%= live_redirect @notebook_info.title, |         <%= live_redirect @notebook_info.title, | ||||||
|  |  | ||||||
|  | @ -63,7 +63,7 @@ defmodule LivebookWeb.ExploreLive do | ||||||
|               </div> |               </div> | ||||||
|             </div> |             </div> | ||||||
|             <div class="flex-grow hidden md:flex flex items-center justify-center"> |             <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> |           </div> | ||||||
|           <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |           <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue