diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js index 76fe451..9f0624b 100644 --- a/assets/tailwind.config.js +++ b/assets/tailwind.config.js @@ -6,10 +6,13 @@ let plugin = require('tailwindcss/plugin') module.exports = { content: [ './js/**/*.js', - '../lib/*_web.ex', - '../lib/*_web/**/*.*ex' + '../lib/sloane_sh/layouts/*.ex', + '../priv/site/**/*.*ex', ], theme: { + container: { + center: true, + }, extend: {}, }, plugins: [ diff --git a/config/config.exs b/config/config.exs index 39e0e36..f63b335 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,13 +1,14 @@ import Config -config :logger, :default_formatter, - format: "$time $metadata[$level] $message\n" +config :logger, :default_formatter, format: "$time $metadata[$level] $message\n" -config :tailwind, version: "3.4.1", default: [ - args: ~w( +config :tailwind, + version: "3.4.1", + default: [ + args: ~w( --config=tailwind.config.js --input=css/app.css --output=../priv/output/assets/app.css ), - cd: Path.expand("../assets", __DIR__) -] + cd: Path.expand("../assets", __DIR__) + ] diff --git a/lib/mix/tasks/site.dev.ex b/lib/mix/tasks/site.dev.ex index ff90ddc..188f8d2 100644 --- a/lib/mix/tasks/site.dev.ex +++ b/lib/mix/tasks/site.dev.ex @@ -6,6 +6,7 @@ defmodule Mix.Tasks.Site.Dev do @impl Mix.Task def run(_args) do Mix.Task.run("app.start") + {:ok, watch_pid} = Task.start_link(fn -> Mix.Task.run("site.watch") diff --git a/lib/sloane_sh/build.ex b/lib/sloane_sh/build.ex index 9130216..13af566 100644 --- a/lib/sloane_sh/build.ex +++ b/lib/sloane_sh/build.ex @@ -1,18 +1,11 @@ defmodule SloaneSH.Build do require Logger - require EEx alias SloaneSH.Context + alias SloaneSH.Layouts alias SloaneSH.Markdown alias SloaneSH.Write - @layouts_dir Path.join(:code.priv_dir(:sloane_sh), "site/layouts") - - EEx.function_from_file(:def, :root_layout, Path.join(@layouts_dir, "root.html.eex"), [ - :ctx, - :inner_content - ]) - def run(%Context{} = ctx) do ctx |> build_pages() @@ -37,8 +30,9 @@ defmodule SloaneSH.Build do path = Path.join(ctx.config.pages_dir, page) with {:ok, data} <- File.read(path), - {:ok, inner_content} <- Markdown.transform(ctx, data), - html = root_layout(ctx, inner_content), + {:ok, md} <- Markdown.transform(ctx, data), + contents = Layouts.page_layout(ctx, md.attrs, md.html), + html = Layouts.root_layout(ctx, md.attrs, contents), :ok <- Write.page(ctx, page, html) do Logger.info("Built page: #{page}") else @@ -50,8 +44,8 @@ defmodule SloaneSH.Build do path = Path.join(ctx.config.posts_dir, post) with {:ok, data} <- File.read(path), - {:ok, html} <- Markdown.transform(ctx, data), - :ok <- Write.post(ctx, post, html) do + {:ok, md} <- Markdown.transform(ctx, data), + :ok <- Write.post(ctx, post, md.html) do Logger.info("Built post: #{post}") else err -> Logger.error("Failed to build post #{post}: #{inspect(err)}") diff --git a/lib/sloane_sh/layouts.ex b/lib/sloane_sh/layouts.ex new file mode 100644 index 0000000..a2c909f --- /dev/null +++ b/lib/sloane_sh/layouts.ex @@ -0,0 +1,27 @@ +defmodule SloaneSH.Layouts do + @moduledoc """ + `EEx` based layouts + """ + require EEx + import SloaneSH.Layouts.Partials + + @layouts_dir Path.join(:code.priv_dir(:sloane_sh), "site/layouts") + + EEx.function_from_file(:def, :root_layout, Path.join(@layouts_dir, "root.html.eex"), [ + :ctx, + :attrs, + :inner_content + ]) + + EEx.function_from_file(:def, :page_layout, Path.join(@layouts_dir, "page.html.eex"), [ + :ctx, + :attrs, + :inner_content + ]) + + EEx.function_from_file(:def, :post_layout, Path.join(@layouts_dir, "post.html.eex"), [ + :ctx, + :attrs, + :inner_content + ]) +end diff --git a/lib/sloane_sh/layouts/partials.ex b/lib/sloane_sh/layouts/partials.ex new file mode 100644 index 0000000..1c1f75c --- /dev/null +++ b/lib/sloane_sh/layouts/partials.ex @@ -0,0 +1,16 @@ +defmodule SloaneSH.Layouts.Partials do + @moduledoc """ + HTML partials for use in HTML layouts + """ + + def header(_ctx) do + ~s""" +
+ +
+ """ + end +end diff --git a/lib/sloane_sh/markdown.ex b/lib/sloane_sh/markdown.ex index a418871..9c6becc 100644 --- a/lib/sloane_sh/markdown.ex +++ b/lib/sloane_sh/markdown.ex @@ -3,19 +3,52 @@ defmodule SloaneSH.Markdown do Markdown parsing using `Earmark` and `Earmark.Parser` """ require Logger + use TypedStruct alias SloaneSH.Context + alias __MODULE__ - def transform(%Context{} = _ctx, data) when is_binary(data) do - case Earmark.as_html(data) do - {:ok, html_doc, deprecation_messages} -> - for msg <- deprecation_messages, do: Logger.warning(msg) + typedstruct do + field :attrs, map(), default: %{} + field :html, String.t(), default: "" + end - {:ok, html_doc} + def transform(%Context{} = ctx, data) when is_binary(data) do + data + |> parse_attrs(ctx) + |> parse_markdown(ctx) + end - {:error, html_doc, error_messages} -> - for msg <- error_messages, do: Logger.error(msg) - {:error, html_doc} + defp parse_attrs("+++" <> rest, _ctx) do + [toml, body] = String.split(rest, ["+++\n", "+++\r\n"], parts: 2) + + with {:ok, attrs} <- Toml.decode(toml, keys: :atoms) do + {:ok, attrs, body} end end + + defp parse_attrs(body, _ctx) do + {:ok, %{}, body} + end + + defp parse_markdown({:ok, attrs, body}, _ctx) do + with {:ok, html, msgs} <- Earmark.as_html(body) do + for msg <- msgs, do: Logger.warning(msg) + + {:ok, + %Markdown{ + attrs: attrs, + html: html + }} + else + {:error, _, msgs} -> + for msg <- msgs, do: Logger.error(msg) + :error + + _ -> + :error + end + end + + defp parse_markdown(other, _ctx), do: other end diff --git a/lib/sloane_sh/watch.ex b/lib/sloane_sh/watch.ex index 87ce35a..11754de 100644 --- a/lib/sloane_sh/watch.ex +++ b/lib/sloane_sh/watch.ex @@ -5,6 +5,7 @@ defmodule SloaneSH.Watch do alias SloaneSH.Build alias SloaneSH.Context + alias SloaneSH.Layouts typedstruct do field :ctx, Context.t(), enforce: true @@ -19,17 +20,21 @@ defmodule SloaneSH.Watch do def init(%Context{} = ctx) do {:ok, watcher_pid} = FileSystem.start_link( - dirs: [ - # ctx.config.layouts_dir, - "priv/site/layouts", - ctx.config.pages_dir, - ctx.config.posts_dir - ] + dirs: + [ + # ctx.config.layouts_dir, + "priv/site/layouts", + ctx.config.pages_dir, + ctx.config.posts_dir + ] + |> dbg() ) - FileSystem.subscribe(watcher_pid) + :ok = FileSystem.subscribe(watcher_pid) - Tailwind.install_and_run(:default, ~w[--watch]) |> dbg() + Task.start_link(fn -> + Tailwind.install_and_run(:default, ~w[--watch]) + end) state = %__MODULE__{ctx: ctx, watcher_pid: watcher_pid} @@ -45,8 +50,10 @@ defmodule SloaneSH.Watch do @impl GenServer def handle_info({:file_event, pid, {path, events}}, %{ctx: ctx, watcher_pid: pid} = state) do + dbg(path) + if String.match?(path, ~r/layouts/) do - recompile_build() + recompile_layouts() Build.run(ctx) {:noreply, state} else @@ -70,9 +77,9 @@ defmodule SloaneSH.Watch do {:stop, :watcher_stopped, pid} end - defp recompile_build do - build_source = Build.module_info(:compile)[:source] |> List.to_string() - {:ok, _, _} = Kernel.ParallelCompiler.compile([build_source]) + defp recompile_layouts do + layouts_source = Layouts.module_info(:compile)[:source] |> List.to_string() + {:ok, _, _} = Kernel.ParallelCompiler.compile([layouts_source]) :ok end diff --git a/mix.exs b/mix.exs index f0eb7cc..04ea82e 100644 --- a/mix.exs +++ b/mix.exs @@ -28,7 +28,8 @@ defmodule SloaneSH.MixProject do {:earmark_parser, "~> 1.4"}, {:plug, "~> 1.15"}, {:bandit, "~> 1.2"}, - {:tailwind, "~> 0.2"} + {:tailwind, "~> 0.2"}, + {:toml, "~> 0.7"} ] end diff --git a/mix.lock b/mix.lock index ccba40b..5ddf801 100644 --- a/mix.lock +++ b/mix.lock @@ -11,6 +11,7 @@ "tailwind": {:hex, :tailwind, "0.2.2", "9e27288b568ede1d88517e8c61259bc214a12d7eed271e102db4c93fcca9b2cd", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "ccfb5025179ea307f7f899d1bb3905cd0ac9f687ed77feebc8f67bdca78565c4"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "thousand_island": {:hex, :thousand_island, "1.3.2", "bc27f9afba6e1a676dd36507d42e429935a142cf5ee69b8e3f90bff1383943cd", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0e085b93012cd1057b378fce40cbfbf381ff6d957a382bfdd5eca1a98eec2535"}, + "toml": {:hex, :toml, "0.7.0", "fbcd773caa937d0c7a02c301a1feea25612720ac3fa1ccb8bfd9d30d822911de", [:mix], [], "hexpm", "0690246a2478c1defd100b0c9b89b4ea280a22be9a7b313a8a058a2408a2fa70"}, "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, } diff --git a/priv/site/layouts/page.html.eex b/priv/site/layouts/page.html.eex new file mode 100644 index 0000000..fb81838 --- /dev/null +++ b/priv/site/layouts/page.html.eex @@ -0,0 +1 @@ +<%= inner_content %> diff --git a/priv/site/layouts/pages.html.eex b/priv/site/layouts/pages.html.eex deleted file mode 100644 index e69de29..0000000 diff --git a/priv/site/layouts/post.html.eex b/priv/site/layouts/post.html.eex new file mode 100644 index 0000000..fb81838 --- /dev/null +++ b/priv/site/layouts/post.html.eex @@ -0,0 +1 @@ +<%= inner_content %> diff --git a/priv/site/layouts/posts.html.eex b/priv/site/layouts/posts.html.eex deleted file mode 100644 index e69de29..0000000 diff --git a/priv/site/layouts/root.html.eex b/priv/site/layouts/root.html.eex index 3eb4fac..d337acb 100644 --- a/priv/site/layouts/root.html.eex +++ b/priv/site/layouts/root.html.eex @@ -1,18 +1,13 @@ - + - sloane.sh + sloane.sh | <%= attrs[:title] %> - -
- -
+ + <%= header(ctx) %> <%= inner_content %> diff --git a/priv/site/pages/index.md b/priv/site/pages/index.md index 23013fa..aea72c0 100644 --- a/priv/site/pages/index.md +++ b/priv/site/pages/index.md @@ -1,6 +1,12 @@ ++++ +permalink = "/" +title = "home" ++++ + # Hello, World! my name is sloane. i am a software engineer. - i'm on the fediverse [@sloane@tech.lgbt](https://tech.lgbt/@sloane) + +also on [github](https://github.com/sloanelybutsurely).