mirror of
				https://github.com/livebook-dev/livebook.git
				synced 2025-10-31 07:46:18 +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 | ||||
|   @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. | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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"> | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue