syntax highlighting and blog/post liveviews working; numerous other styling updates
This commit is contained in:
parent
a6186ba639
commit
2218a678b1
35 changed files with 610 additions and 120 deletions
|
@ -8,7 +8,9 @@ defmodule Home73k do
|
|||
"""
|
||||
|
||||
@app_vars Application.compile_env(:home73k, :app_global_vars, time_zone: "America/New_York")
|
||||
@app_time_zone @app_vars[:time_zone]
|
||||
|
||||
def app_time_zone, do: @app_time_zone
|
||||
def app_vars, do: @app_vars
|
||||
def app_time_zone, do: @app_vars[:time_zone]
|
||||
def app_blog_content, do: @app_vars[:blog_content]
|
||||
def app_pygmentize_bin, do: @app_vars[:pygmentize_bin]
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ defmodule Home73k.Blog do
|
|||
|
||||
Application.ensure_all_started(:earmark)
|
||||
|
||||
posts_paths = "priv/content/**/*.md" |> Path.wildcard()
|
||||
posts_paths = "#{Home73k.app_blog_content()}/**/*.md" |> Path.wildcard()
|
||||
|
||||
posts =
|
||||
for post_path <- posts_paths do
|
||||
|
@ -11,7 +11,7 @@ defmodule Home73k.Blog do
|
|||
Post.parse!(post_path)
|
||||
end
|
||||
|
||||
@posts Enum.sort_by(posts, & &1.date, {:desc, NaiveDateTime})
|
||||
@posts Enum.sort_by(posts, & &1.date, {:desc, Date})
|
||||
|
||||
@tags posts |> Stream.flat_map(& &1.tags) |> Stream.uniq() |> Enum.sort()
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
defmodule Home73k.Blog.Post do
|
||||
alias Home73k.Highlighter
|
||||
|
||||
@enforce_keys [:title, :id, :date, :author, :tags, :lede, :body, :corpus]
|
||||
defstruct [:title, :id, :date, :author, :tags, :lede, :body, :corpus]
|
||||
|
||||
|
@ -59,12 +61,10 @@ defmodule Home73k.Blog.Post do
|
|||
defp parse_lede(_), do: nil
|
||||
|
||||
# """ parse_body/1
|
||||
# Convert body markdown to html
|
||||
# TODO: handle syntax highlighting
|
||||
# Convert body markdown to html, and highlight code fence blocks
|
||||
defp parse_body({fm, md}) do
|
||||
Map.put(fm, :body, Earmark.as_html!(md))
|
||||
# TODO: Earmark.as_ast(md) |> parse_body(fm)
|
||||
# def parse_body({:ok, ast, _}, fm)
|
||||
html = Earmark.as_html!(md) |> Highlighter.highlight_code_blocks()
|
||||
Map.put(fm, :body, html)
|
||||
end
|
||||
|
||||
defp parse_body(_), do: nil
|
||||
|
@ -128,8 +128,10 @@ defmodule Home73k.Blog.Post do
|
|||
# Handle split of post body. If lede found, return as html with body.
|
||||
# Otherwise return nil with body.
|
||||
# """
|
||||
defp extract_lede([lede, body]),
|
||||
do: {String.trim_trailing(lede) |> Earmark.as_html!(), String.trim_leading(body)}
|
||||
defp extract_lede([lede, body]) do
|
||||
lede_html = String.trim_trailing(lede) |> Earmark.as_html!() |> Highlighter.highlight_code_blocks()
|
||||
{lede_html, String.trim_leading(body)}
|
||||
end
|
||||
|
||||
defp extract_lede([body]), do: {nil, body}
|
||||
|
||||
|
|
52
lib/home73k/highlighter.ex
Normal file
52
lib/home73k/highlighter.ex
Normal file
|
@ -0,0 +1,52 @@
|
|||
defmodule Home73k.Highlighter do
|
||||
@moduledoc """
|
||||
Performs code highlighting.
|
||||
"""
|
||||
|
||||
alias Home73k.Temp
|
||||
|
||||
@pygments_cmd Home73k.app_pygmentize_bin() |> Path.expand()
|
||||
|
||||
@doc """
|
||||
Highlights all code block in an already generated HTML document.
|
||||
"""
|
||||
def highlight_code_blocks(html) do
|
||||
~r/<pre><code(?:\s+class="(\w*)")?>([^<]*)<\/code><\/pre>/
|
||||
|> Regex.replace(html, &highlight_code_block(&1, &2, &3))
|
||||
end
|
||||
|
||||
defp highlight_code_block(_full_block, lang, code) do
|
||||
# unescape the code
|
||||
unescaped_code = unescape_html(code) |> IO.iodata_to_binary()
|
||||
|
||||
# write code to temp file
|
||||
tmp_file = Temp.file()
|
||||
File.write!(tmp_file, unescaped_code)
|
||||
|
||||
# pygmentize the code via temp file
|
||||
pyg_args = ["-l", lang, "-f", "html", "-O", "cssclass=pygments", tmp_file]
|
||||
{highlighted, _} = System.cmd(@pygments_cmd, pyg_args)
|
||||
|
||||
# correct pygment wrapping markup
|
||||
highlighted
|
||||
|> String.replace("<span></span>", "")
|
||||
|> String.replace("<div class=\"pygments\"><pre>", "<pre class=\"pygments\"><code class=\"language-#{lang}\">")
|
||||
|> String.replace("</pre></div>", "</code></pre>")
|
||||
end
|
||||
|
||||
entities = [{"&", ?&}, {"<", ?<}, {">", ?>}, {""", ?"}, {"'", ?'}]
|
||||
|
||||
for {encoded, decoded} <- entities do
|
||||
defp unescape_html(unquote(encoded) <> rest) do
|
||||
[unquote(decoded) | unescape_html(rest)]
|
||||
end
|
||||
end
|
||||
|
||||
defp unescape_html(<<c, rest::binary>>) do
|
||||
[c | unescape_html(rest)]
|
||||
end
|
||||
|
||||
defp unescape_html(<<>>) do
|
||||
[]
|
||||
end
|
||||
end
|
19
lib/home73k/temp.ex
Normal file
19
lib/home73k/temp.ex
Normal file
|
@ -0,0 +1,19 @@
|
|||
defmodule Home73k.Temp do
|
||||
@moduledoc """
|
||||
Simple module to generate temporary files
|
||||
"""
|
||||
def file do
|
||||
System.tmp_dir!()
|
||||
|> Path.join(random_filename())
|
||||
|> touch_file()
|
||||
end
|
||||
|
||||
defp random_filename do
|
||||
:crypto.strong_rand_bytes(32) |> Base.url_encode64 |> binary_part(0, 32)
|
||||
end
|
||||
|
||||
defp touch_file(fdname) do
|
||||
File.touch!(fdname)
|
||||
fdname
|
||||
end
|
||||
end
|
|
@ -93,6 +93,9 @@ defmodule Home73kWeb do
|
|||
# Import SVG Icon helper
|
||||
import Home73kWeb.IconHelpers
|
||||
|
||||
# Import Date formatter helper
|
||||
import Home73kWeb.DateHelpers
|
||||
|
||||
import Home73kWeb.ErrorHelpers
|
||||
import Home73kWeb.Gettext
|
||||
alias Home73kWeb.Router.Helpers, as: Routes
|
||||
|
|
|
@ -48,7 +48,4 @@ defmodule Home73kWeb.BlogLive do
|
|||
# do: {app, vsn}
|
||||
# end
|
||||
|
||||
defp format_date(date) do
|
||||
Calendar.strftime(date, "%B %-d, %Y")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
<main class="container d-flex justify-content-center">
|
||||
|
||||
<div class="col-12 col-md-10 col-lg-8 col-xl-7 col-xxl-6 pb-2 mb-4 mt-3">
|
||||
<div class="col-12 col-md-10 col-lg-9 col-xl-8 col-xxl-7 pb-2 mb-4 mt-3">
|
||||
|
||||
<%= for post <- @posts do %>
|
||||
|
||||
<div class="post border-bottom border-gray pb-4 mb-3">
|
||||
|
||||
<h2 class="post-title fs-2 fw-normal mb-2">
|
||||
<h2 class="post-title fs-2 fw-600 mb-2">
|
||||
<%= live_redirect "#{post.title}", to: Routes.post_path(@socket, :show, post) %>
|
||||
</h2>
|
||||
|
||||
<div class="post-date font-monospace text-gray-500 <%= if length(post.tags) == 0, do: "mb-3" %>">
|
||||
<%= icon_div @socket, "mdi-calendar-clock", [class: "icon baseline"] %>
|
||||
<%= post.date |> format_date() %>
|
||||
<div class="post-date font-monospace text-gray-400 <%= if length(post.tags) == 0, do: "mb-3" %>">
|
||||
<%= icon_div @socket, "mdi-calendar-clock", [class: "icon baseline me-2"] %><%= post.date |> format_date() %>
|
||||
by <%= icon_div @socket, "mdi-account", [class: "icon baseline me-1"] %>Adam Piontek
|
||||
</div>
|
||||
|
||||
<%= if length(post.tags) > 0 do %>
|
||||
<div class="post-tags fs-6 mb-3">
|
||||
<div class="post-tags fs-smaller mb-3">
|
||||
<%= icon_div @socket, "mdi-tag-multiple", [class: "icon baseline"] %>
|
||||
<%= for {tag, i} <- Enum.with_index(post.tags) do %>
|
||||
#<%= tag %><%= i < (length(post.tags) - 1) && "," || "" %>
|
||||
|
@ -24,7 +24,7 @@
|
|||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="post-lede lead">
|
||||
<div class="post-lede">
|
||||
<%= raw post.lede %>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -55,7 +55,4 @@ defmodule Home73kWeb.PostLive do
|
|||
# do: {app, vsn}
|
||||
# end
|
||||
|
||||
defp format_date(date) do
|
||||
Calendar.strftime(date, "%B %-d, %Y")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
<h2 class="post-title fs-2 fw-normal mb-2"><%= raw @post.title %></h2>
|
||||
|
||||
<div class="post-date font-monospace text-gray-500 <%= if length(@post.tags) == 0, do: "mb-3" %>">
|
||||
<%= icon_div @socket, "mdi-calendar-clock", [class: "icon baseline"] %>
|
||||
<%= @post.date |> format_date() %>
|
||||
<div class="post-date font-monospace text-gray-400 <%= if length(@post.tags) == 0, do: "mb-3" %>">
|
||||
<%= icon_div @socket, "mdi-calendar-clock", [class: "icon baseline me-2"] %><%= @post.date |> format_date() %>
|
||||
by <%= icon_div @socket, "mdi-account", [class: "icon baseline me-1"] %>Adam Piontek
|
||||
</div>
|
||||
|
||||
<%= if length(@post.tags) > 0 do %>
|
||||
<div class="post-tags fs-6 mb-3">
|
||||
<div class="post-tags fs-smaller mb-3">
|
||||
<%= icon_div @socket, "mdi-tag-multiple", [class: "icon baseline"] %>
|
||||
<%= for {tag, i} <- Enum.with_index(@post.tags) do %>
|
||||
#<%= tag %><%= i < (length(@post.tags) - 1) && "," || "" %>
|
||||
|
@ -20,7 +20,7 @@
|
|||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="post-lede lead">
|
||||
<div class="post-lede">
|
||||
<%= raw @post.lede %>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<main class="container d-flex justify-content-center">
|
||||
|
||||
<div class="col-12 col-md-10 col-lg-8 col-xl-7 col-xxl-6 border-bottom border-gray pb-3 mb-5 mt-3">
|
||||
<div class="col-12 col-md-10 col-lg-9 col-xl-8 col-xxl-7 border-bottom border-gray pb-3 mb-5 mt-3">
|
||||
|
||||
<h2 class="fs-2 fw-600 mb-0">About</h2>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
<div class="col-12 col-sm-10 col-md-7 col-lg-6 col-xl-5 col-xxl-4 justify-content-start ms-lg-3">
|
||||
|
||||
<h2 class="font-monospace fs-2 fw-500 mb-0">
|
||||
<h2 class="fs-2 fw-600 mb-0">
|
||||
<%= icon_div @conn, "mdi-account-hard-hat", [class: "icon baseline me-2"] %><span>Working on it!</span>
|
||||
</h2>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
<div class="col-auto justify-content-start ms-lg-3">
|
||||
|
||||
<h2 class="font-monospace fs-2 fw-500 mb-0">
|
||||
<h2 class="fs-2 fw-600 mb-0">
|
||||
<%= icon_div @conn, "mdi-account", [class: "icon baseline me-2"] %><span>Adam Piontek</span>
|
||||
</h2>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
<div class="col-auto justify-content-start">
|
||||
|
||||
<h2 class="font-monospace fs-2 fw-500 mb-0">
|
||||
<h2 class="fs-2 fw-600 mb-0">
|
||||
<%= icon_div @conn, "mdi-account", [class: "icon baseline me-2"] %><span>Adam Piontek</span>
|
||||
</h2>
|
||||
|
||||
|
|
9
lib/home73k_web/views/date_helpers.ex
Normal file
9
lib/home73k_web/views/date_helpers.ex
Normal file
|
@ -0,0 +1,9 @@
|
|||
defmodule Home73kWeb.DateHelpers do
|
||||
@moduledoc """
|
||||
Formatters for dates
|
||||
"""
|
||||
|
||||
def format_date(date) do
|
||||
Calendar.strftime(date, "%B %-d, %Y")
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue