improvement: monitoring/images/cleanup

This commit is contained in:
Zach Daniel 2022-09-12 19:36:28 -04:00
parent 21a0c8aa44
commit f515484441
17 changed files with 304 additions and 252 deletions

View file

@ -227,7 +227,7 @@ let topBarScheduled = undefined;
window.addEventListener("phx:page-loading-start", () => {
scrolled = false;
if (!topBarScheduled) {
topBarScheduled = setTimeout(() => topbar.show(), 120);
topBarScheduled = setTimeout(() => topbar.show(), 250);
}
});

View file

@ -0,0 +1,30 @@
defmodule AshHqWeb.Components.Docs.DocPath do
use Surface.Component
alias AshHqWeb.Components.CalloutText
prop doc_path, :list, required: true
def render(assigns) do
~F"""
<div class="flex flex-row space-x-1 items-center">
{#case @doc_path}
{#match [item]}
<div class="dark:text-white">
{item}
</div>
{#match path}
{#for item <- :lists.droplast(path)}
<span class="text-base-light-400">
{item}
</span>
<Heroicons.Outline.ChevronRightIcon class="w-3 h-3" />
{/for}
<span class="dark:text-white">
<CalloutText text={List.last(path)} />
</span>
{/case}
</div>
"""
end
end

View file

@ -0,0 +1,44 @@
defmodule AshHqWeb.Components.Docs.Functions do
use Surface.Component
import AshHqWeb.Helpers
alias AshHqWeb.Components.Docs.SourceLink
prop type, :atom, required: true
prop functions, :list, required: true
prop header, :string, required: true
prop library, :any, required: true
prop library_version, :any, required: true
prop libraries, :list, required: true
prop selected_versions, :map, required: true
def render(assigns) do
~F"""
{#case Enum.filter(@functions, &(&1.type == @type))}
{#match []}
{#match functions}
<h1>{@header}</h1>
{#for function <- functions}
<div id={"#{@type}-#{function.sanitized_name}-#{function.arity}"} class="nav-anchor rounded-lg bg-base-dark-400 dark:bg-base-dark-700 bg-opacity-50 px-2">
<p class="">
<div class="">
<div class="flex flex-row items-baseline">
<a href={"##{@type}-#{function.sanitized_name}-#{function.arity}"}>
<Heroicons.Outline.LinkIcon class="h-3 m-3" />
</a>
<div class="text-xl font-semibold mb-2">{function.name}/{function.arity} <SourceLink module_or_function={function} library={@library} library_version={@library_version} />
</div>
</div>
{#for head <- function.heads}
<code class="makeup elixir">{head}</code>
{/for}
{raw(render_replacements(@libraries, @selected_versions, function.html_for))}
</div>
</p>
</div>
{/for}
{/case}
"""
end
end

View file

@ -0,0 +1,16 @@
defmodule AshHqWeb.Components.Docs.SourceLink do
use Surface.Component
import AshHqWeb.Helpers
prop module_or_function, :any, required: true
prop library, :any, required: true
prop library_version, :any, required: true
def render(assigns) do
~F"""
{#if @module_or_function.file}
<a target="_blank" href={source_link(@module_or_function, @library, @library_version)}>{"</>"}</a>
{/if}
"""
end
end

View file

@ -15,7 +15,7 @@ defmodule AshHqWeb.Components.RightNav do
<a
id={"right-nav-callback-#{function.sanitized_name}-#{function.arity}"}
class="hover:text-primary-light-300 right-nav"
href={"#function-#{function.sanitized_name}-#{function.arity}"}
href={"#callback-#{function.sanitized_name}-#{function.arity}"}
>
{"#{function.name}/#{function.arity}"}
</a>

View file

@ -5,27 +5,23 @@ defmodule AshHqWeb.Components.VersionPills do
alias Surface.Components.Form
alias Surface.Components.Form.Select
prop(selected_versions, :map, default: %{})
prop(libraries, :list, default: [])
prop(add_version, :event)
prop(remove_version, :event)
prop(change_version, :event)
prop(editable, :boolean, default: true)
prop selected_versions, :map, default: %{}
prop libraries, :list, default: []
prop add_version, :event
prop remove_version, :event
prop change_version, :event
prop editable, :boolean, default: true
data(adding_version, :boolean, default: false)
data adding_version, :boolean, default: false
def render(assigns) do
~F"""
<div class="flex flex-row flex-wrap align-center items-center ml-2 justify-star gap-2 flex-grow">
{#for library <- @libraries}
{selected_version = @selected_versions[library.id]
nil}
{#if selected_version not in [nil, ""]}
{version_name = selected_version(library, selected_version)
nil}
{#if @selected_versions[library.id] not in [nil, ""]}
<div class="flex flex-row flex-wrap contents-center px-2 py-1 bg-primary-light-500 dark:bg-primary-light-400 hover:bg-primary-light-600 text-black text-xs font-medium rounded-full">
{library.name}{#if version_name != "latest"}
| {version_name}
{library.name}{#if selected_version(library, @selected_versions[library.id]) != "latest"}
| {selected_version(library, @selected_versions[library.id])}
{/if}
{#if @editable}
<button :on-click={@remove_version} phx-value-library={library.id}><Heroicons.Outline.XIcon class="h-3 w-3 ml-1" /></button>

View file

@ -1,7 +1,179 @@
defmodule AshHqWeb.Helpers do
@moduledoc "Simple helpers for doc liveviews"
require Logger
alias AshHqWeb.DocRoutes
def latest_version(library) do
Enum.at(library.versions, 0)
end
def source_link(%AshHq.Docs.Module{file: file}, library, library_version) do
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}"
end
def source_link(%AshHq.Docs.Function{file: file, line: line}, library, library_version) do
if line do
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}#L#{line}"
else
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}"
end
end
def render_replacements(_, _, nil), do: ""
def render_replacements(libraries, selected_versions, docs) do
docs
|> render_links(libraries, selected_versions)
|> render_mix_deps(libraries, selected_versions)
end
defp render_mix_deps(docs, libraries, selected_versions) do
String.replace(docs, ~r/(?!<code>){{mix_dep:.*}}(?!<\/code>)/, fn text ->
try do
"{{mix_dep:" <> library = String.trim_trailing(text, "}}")
"<pre><code>#{render_mix_dep(libraries, library, selected_versions, text)}</code></pre>"
rescue
e ->
IO.inspect(__STACKTRACE__)
Logger.error(
"Invalid link #{Exception.format(:error, e)}\n#{Exception.format_stacktrace(__STACKTRACE__)}"
)
text
end
end)
end
defp render_mix_dep(libraries, library, selected_versions, source) do
library =
Enum.find(libraries, &(&1.name == library)) ||
raise "No such library in link: #{source}"
version =
if selected_versions[library.id] == "latest" do
AshHqWeb.Helpers.latest_version(library)
else
case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do
nil ->
nil
version ->
version
end
end
case Version.parse(version.version) do
{:ok, %Version{pre: pre, build: build}}
when not is_nil(pre) or not is_nil(build) ->
~s({:#{library.name}, "~> #{version.version}"})
{:ok, %Version{major: major, minor: minor, patch: 0}} ->
~s({:#{library.name}, "~> #{major}.#{minor}"})
{:ok, version} ->
~s({:#{library.name}, "~> #{version.version}"})
end
end
def render_links(docs, libraries, selected_versions) do
String.replace(docs, ~r/(?!<code>){{link:[^}]*}}(?!<\/code>)/, fn text ->
try do
"{{link:" <> rest = String.trim_trailing(text, "}}")
[library, type, item | rest] = String.split(rest, ":")
render_link(libraries, selected_versions, library, type, item, text, rest)
rescue
e ->
IO.inspect(__STACKTRACE__)
Logger.error(
"Invalid link #{Exception.format(:error, e)}\n#{Exception.format_stacktrace(__STACKTRACE__)}"
)
text
end
end)
end
defp render_link(libraries, selected_versions, library, type, item, source, rest) do
library =
Enum.find(libraries, &(&1.name == library)) ||
raise "No such library in link: #{source}"
version =
if selected_versions[library.id] in ["latest", nil, ""] do
Enum.find(library.versions, &String.contains?(&1.version, ".")) ||
AshHqWeb.Helpers.latest_version(library)
else
case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do
nil ->
nil
version ->
version
end
end
if is_nil(version) do
raise "no version for library"
else
case type do
"guide" ->
guide =
Enum.find(version.guides, &(&1.name == item)) ||
raise "No such guide in link: #{source}"
text = Enum.at(rest, 0) || item
"""
<a href="#{DocRoutes.doc_link(guide, selected_versions)}">#{text}</a>
"""
"dsl" ->
path =
item
|> String.split(~r/[\/\.]/)
name =
path
|> Enum.join(".")
route = Enum.map_join(path, "/", &DocRoutes.sanitize_name/1)
"""
<a href="/docs/dsl/#{library.name}/#{version.version}/#{route}">#{name}</a>
"""
"option" ->
path =
item
|> String.split(~r/[\/\.]/)
name = Enum.join(path, ".")
dsl_path = path |> :lists.droplast() |> Enum.map_join("/", &DocRoutes.sanitize_name/1)
anchor = path |> Enum.map_join("/", &DocRoutes.sanitize_name/1)
"""
<a href="/docs/dsl/#{library.name}/#{version.version}/#{dsl_path}##{anchor}">#{name}</a>
"""
"module" ->
"""
<a href="/docs/module/#{library.name}/#{version.version}/#{DocRoutes.sanitize_name(item)}">#{item}</a>
"""
"extension" ->
"""
<a href="/docs/dsl/#{library.name}/#{version.version}/#{DocRoutes.sanitize_name(item)}">#{item}</a>
"""
type ->
raise "unimplemented link type #{inspect(type)} in #{source}"
end
end
end
end

View file

@ -2,7 +2,10 @@ defmodule AshHqWeb.Pages.Docs do
@moduledoc "The page for showing documentation"
use Surface.LiveComponent
import AshHqWeb.Helpers
alias AshHqWeb.Components.{CalloutText, DocSidebar, RightNav, Tag}
alias AshHqWeb.Components.Docs.{DocPath, Functions, SourceLink}
alias AshHqWeb.DocRoutes
alias Phoenix.LiveView.JS
require Logger
@ -40,24 +43,7 @@ defmodule AshHqWeb.Pages.Docs do
<Heroicons.Outline.MenuIcon class="w-8 h-8 ml-4" />
</button>
{#if @doc_path && @doc_path != []}
<div class="flex flex-row space-x-1 items-center">
{#case @doc_path}
{#match [item]}
<div class="dark:text-white">
{item}
</div>
{#match path}
{#for item <- :lists.droplast(path)}
<span class="text-base-light-400">
{item}
</span>
<Heroicons.Outline.ChevronRightIcon class="w-3 h-3" />
{/for}
<span class="dark:text-white">
<CalloutText text={List.last(path)} />
</span>
{/case}
</div>
<DocPath doc_path={@doc_path}/>
{/if}
</div>
<span class="grid overflow-hidden xl:hidden z-40">
@ -112,7 +98,7 @@ defmodule AshHqWeb.Pages.Docs do
class="w-full nav-anchor text-black dark:text-white relative py-4 md:py-auto"
>
{#if @module}
<h2>{@module.name}{render_source_code_link(assigns, @module, @library, @library_version)}</h2>
<h2>{@module.name} <SourceLink module_or_function={@module} library={@library} library_version={@library_version}/></h2>
{/if}
{#if @library_version}
<div class="static mb-6 md:absolute right-2 top-2 border rounded-lg flex flex-row w-fit">
@ -124,14 +110,14 @@ defmodule AshHqWeb.Pages.Docs do
</div>
</div>
{/if}
{raw(render_replacements(assigns, @docs))}
{raw(render_replacements(@libraries, @selected_versions, @docs))}
{#if @dsl}
{#for {category, links} <- @dsl.links || %{}}
<h3>{String.capitalize(category)}</h3>
<ul>
{#for link <- links}
<li>
{raw(render_links("{{link:#{link}}}", assigns))}
{raw(render_links("{{link:#{link}}}", @libraries, @selected_versions))}
</li>
{/for}
</ul>
@ -139,9 +125,9 @@ defmodule AshHqWeb.Pages.Docs do
{/if}
</div>
{#if @module}
{render_functions(assigns, @module.functions, :callback, "Callbacks")}
{render_functions(assigns, @module.functions, :function, "Functions")}
{render_functions(assigns, @module.functions, :macro, "Macros")}
<Functions header="Callbacks" type={:callback} functions={@module.functions} library={@library} library_version={@library_version} libraries={@libraries} selected_versions={@selected_versions} />
<Functions header="Functions" type={:function} functions={@module.functions} library={@library} library_version={@library_version} libraries={@libraries} selected_versions={@selected_versions} />
<Functions header="Macros" type={:macro} functions={@module.functions} library={@library} library_version={@library_version} libraries={@libraries} selected_versions={@selected_versions} />
{/if}
{#case modules_in_scope(@dsl, @extension, @libraries, @selected_versions)}
{#match []}
@ -201,14 +187,14 @@ defmodule AshHqWeb.Pages.Docs do
{option.type}
</td>
<td>
{raw(render_replacements(assigns, option.html_for))}
{raw(render_replacements(@libraries, @selected_versions, option.html_for))}
</td>
<td>
{raw(
Enum.map_join(
List.flatten(Map.values(option.links || %{})),
", ",
&render_links("{{link:#{&1}}}", assigns)
&render_links("{{link:#{&1}}}", @libraries, @selected_versions)
)
)}
</td>
@ -243,14 +229,14 @@ defmodule AshHqWeb.Pages.Docs do
{option.type}
</td>
<td>
{raw(render_replacements(assigns, option.html_for))}
{raw(render_replacements(@libraries, @selected_versions, option.html_for))}
</td>
<td>
{raw(
Enum.map_join(
List.flatten(Map.values(option.links || %{})),
", ",
&render_links("{{link:#{&1}}}", assigns)
&render_links("{{link:#{&1}}}", @libraries, @selected_versions)
)
)}
</td>
@ -276,14 +262,6 @@ defmodule AshHqWeb.Pages.Docs do
{:ok, socket |> assign(assigns) |> load_docs()}
end
defp render_source_code_link(assigns, module_or_function, library, library_version) do
~F"""
{#if module_or_function.file}
<a target="_blank" href={source_link(module_or_function, library, library_version)}>{"</>"}</a>
{/if}
"""
end
defp modules_in_scope(nil, _, _, _), do: []
defp modules_in_scope(_, nil, _, _), do: []
@ -340,46 +318,6 @@ defmodule AshHqWeb.Pages.Docs do
|> Enum.sort_by(& &1.argument_index)
end
defp render_functions(assigns, functions, type, header) do
~F"""
{#case Enum.filter(functions, &(&1.type == type))}
{#match []}
{#match functions}
<h1>{header}</h1>
{#for function <- functions}
<div id={"#{type}-#{function.sanitized_name}-#{function.arity}"} class="nav-anchor rounded-lg bg-base-dark-400 dark:bg-base-dark-700 bg-opacity-50 px-2">
<p class="">
<div class="">
<div class="flex flex-row items-baseline">
<a href={"##{type}-#{function.sanitized_name}-#{function.arity}"}>
<Heroicons.Outline.LinkIcon class="h-3 m-3" />
</a>
<div class="text-xl font-semibold mb-2">{function.name}/{function.arity} {render_source_code_link(assigns, function, @library, @library_version)}</div>
</div>
</div>
{#for head <- function.heads}
<code class="makeup elixir">{head}</code>
{/for}
{raw(render_replacements(assigns, function.html_for))}
</p>
</div>
{/for}
{/case}
"""
end
defp source_link(%AshHq.Docs.Module{file: file}, library, library_version) do
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}"
end
defp source_link(%AshHq.Docs.Function{file: file, line: line}, library, library_version) do
if line do
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}#L#{line}"
else
"https://github.com/ash-project/#{library.name}/tree/v#{library_version.version}/#{file}"
end
end
def path_to_name(path, name) do
Enum.map_join(path ++ [name], "-", &DocRoutes.sanitize_name/1)
end
@ -411,163 +349,9 @@ defmodule AshHqWeb.Pages.Docs do
)
end
defp render_replacements(_assigns, nil), do: ""
def load_docs(socket) do
start = IO.inspect(System.monotonic_time())
defp render_replacements(assigns, docs) do
docs
|> render_links(assigns)
|> render_mix_deps(assigns)
end
defp render_mix_deps(docs, assigns) do
String.replace(docs, ~r/(?!<code>){{mix_dep:.*}}(?!<\/code>)/, fn text ->
try do
"{{mix_dep:" <> library = String.trim_trailing(text, "}}")
"<pre><code>#{render_mix_dep(assigns, library, text)}</code></pre>"
rescue
e ->
Logger.error(
"Invalid link #{inspect(e)}\n#{Exception.format_stacktrace(__STACKTRACE__)}"
)
text
end
end)
end
defp render_mix_dep(assigns, library, source) do
library =
Enum.find(assigns[:libraries], &(&1.name == library)) ||
raise "No such library in link: #{source}"
selected_versions = assigns[:selected_versions]
version =
if selected_versions[library.id] == "latest" do
AshHqWeb.Helpers.latest_version(library)
else
case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do
nil ->
nil
version ->
version
end
end
case Version.parse(version.version) do
{:ok, %Version{pre: pre, build: build}}
when not is_nil(pre) or not is_nil(build) ->
~s({:#{library.name}, "~> #{version}"})
{:ok, %Version{major: major, minor: minor, patch: 0}} ->
~s({:#{library.name}, "~> #{major}.#{minor}"})
{:ok, version} ->
~s({:#{library.name}, "~> #{version}"})
end
end
defp render_links(docs, assigns) do
String.replace(docs, ~r/(?!<code>){{link:[^}]*}}(?!<\/code>)/, fn text ->
try do
"{{link:" <> rest = String.trim_trailing(text, "}}")
[library, type, item | rest] = String.split(rest, ":")
render_link(assigns, library, type, item, text, rest)
rescue
e ->
Logger.error(
"Invalid link #{inspect(e)}\n#{Exception.format_stacktrace(__STACKTRACE__)}"
)
text
end
end)
end
defp render_link(assigns, library, type, item, source, rest) do
library =
Enum.find(assigns[:libraries], &(&1.name == library)) ||
raise "No such library in link: #{source}"
selected_versions = assigns[:selected_versions]
version =
if selected_versions[library.id] in ["latest", nil, ""] do
Enum.find(library.versions, &String.contains?(&1.version, ".")) ||
AshHqWeb.Helpers.latest_version(library)
else
case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do
nil ->
nil
version ->
version
end
end
if is_nil(version) do
raise "no version for library"
else
case type do
"guide" ->
guide =
Enum.find(version.guides, &(&1.name == item)) ||
raise "No such guide in link: #{source}"
text = Enum.at(rest, 0) || item
"""
<a href="#{DocRoutes.doc_link(guide, assigns[:selected_versions])}">#{text}</a>
"""
"dsl" ->
path =
item
|> String.split(~r/[\/\.]/)
name =
path
|> Enum.join(".")
route = Enum.map_join(path, "/", &DocRoutes.sanitize_name/1)
"""
<a href="/docs/dsl/#{library.name}/#{version.version}/#{route}">#{name}</a>
"""
"option" ->
path =
item
|> String.split(~r/[\/\.]/)
name = Enum.join(path, ".")
dsl_path = path |> :lists.droplast() |> Enum.map_join("/", &DocRoutes.sanitize_name/1)
anchor = path |> Enum.map_join("/", &DocRoutes.sanitize_name/1)
"""
<a href="/docs/dsl/#{library.name}/#{version.version}/#{dsl_path}##{anchor}">#{name}</a>
"""
"module" ->
"""
<a href="/docs/module/#{library.name}/#{version.version}/#{DocRoutes.sanitize_name(item)}">#{item}</a>
"""
"extension" ->
"""
<a href="/docs/dsl/#{library.name}/#{version.version}/#{DocRoutes.sanitize_name(item)}">#{item}</a>
"""
type ->
raise "unimplemented link type #{inspect(type)} in #{source}"
end
end
end
defp load_docs(socket) do
new_libraries =
socket.assigns.libraries
|> Enum.map(fn library ->
@ -575,8 +359,7 @@ defmodule AshHqWeb.Pages.Docs do
Map.update!(library, :versions, fn versions ->
Enum.map(versions, fn version ->
if (socket.assigns[:selected_versions][library.id] in ["latest", nil, ""] &&
latest_version &&
if (latest_version &&
version.id == latest_version.id) ||
version.id == socket.assigns[:selected_versions][library.id] do
dsls_query =
@ -631,6 +414,10 @@ defmodule AshHqWeb.Pages.Docs do
|> assign_module()
|> assign_dsl()
|> assign_docs()
|> tap(fn stuff ->
IO.inspect(System.convert_time_unit(System.monotonic_time() - start, :native, :millisecond))
stuff
end)
end
defp load_for_search(query, docs_for) do

View file

@ -112,6 +112,9 @@ defmodule AshHqWeb.Router do
live_dashboard "/dashboard",
metrics: AshHqWeb.Telemetry,
additional_pages: [
flame_on: FlameOn.DashboardPage
],
ecto_repos: [AshHq.Repo],
ecto_psql_extras_options: [long_running_queries: [threshold: "200 milliseconds"]]
end

View file

@ -60,6 +60,10 @@ defmodule AshHq.MixProject do
{:bcrypt_elixir, "~> 3.0"},
# CSP
{:plug_content_security_policy, "~> 0.2.1"},
# Live Dashboard
{:flame_on, "~> 0.5.0"},
{:phoenix_live_dashboard, "~> 0.6"},
{:ecto_psql_extras, "~> 0.6"},
# Phoenix/Core dependencies
{:phoenix, "~> 1.6.6"},
{:phoenix_ecto, "~> 4.4"},
@ -71,8 +75,6 @@ defmodule AshHq.MixProject do
{:nimble_options, "~> 0.4.0", override: true},
{:finch, "~> 0.10.2"},
{:floki, ">= 0.30.0"},
{:phoenix_live_dashboard, "~> 0.6"},
{:ecto_psql_extras, "~> 0.6"},
{:esbuild, "~> 0.3", runtime: Mix.env() == :dev},
{:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"},

View file

@ -32,6 +32,7 @@
"excoveralls": {:hex, :excoveralls, "0.14.6", "610e921e25b180a8538229ef547957f7e04bd3d3e9a55c7c5b7d24354abbba70", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "0eceddaa9785cfcefbf3cd37812705f9d8ad34a758e513bb975b081dce4eb11e"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"finch": {:hex, :finch, "0.10.2", "9ad27d68270d879f73f26604bb2e573d40f29bf0e907064a9a337f90a16a0312", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dd8b11b282072cec2ef30852283949c248bd5d2820c88d8acc89402b81db7550"},
"flame_on": {:hex, :flame_on, "0.5.2", "31b3f417418f49956e7f8c9b10098ecadc2d529d7a2fcaf17bbe65d78462d763", [:mix], [{:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}, {:phoenix_ecto, "~> 4.4", [hex: :phoenix_ecto, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "~> 0.6.4", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.6", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "552b0013640089d07ae03bee93c6c3f0803adf1a001138ee7ec06e72b7da3756"},
"floki": {:hex, :floki, "0.33.1", "f20f1eb471e726342b45ccb68edb9486729e7df94da403936ea94a794f072781", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "461035fd125f13fdf30f243c85a0b1e50afbec876cbf1ceefe6fddd2e6d712c6"},
"getopt": {:hex, :getopt, "1.0.1", "c73a9fa687b217f2ff79f68a3b637711bb1936e712b521d8ce466b29cbf7808a", [:rebar3], [], "hexpm", "53e1ab83b9ceb65c9672d3e7a35b8092e9bdc9b3ee80721471a161c10c59959c"},
"gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"},
@ -51,6 +52,7 @@
"makeup_html": {:hex, :makeup_html, "0.1.0", "b0228fda985e311d8f0d25bed58f8280826633a38d7448cabdd723e116165bcf", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "0ca44e7dcb8d933e010740324470dd8ec947243b51304bd34b8165ef3281edc2"},
"makeup_js": {:hex, :makeup_js, "0.1.0", "ffa8ce9db95d14dcd09045334539d5992d540d63598c592d4805b7674bdd6675", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "3f0c1a5eb52c9737b1679c926574e83bb260ccdedf08b58ee96cca7c685dea75"},
"makeup_sql": {:hex, :makeup_sql, "0.1.0", "197a8a0a38e83885f73767530739bb8f990aecf7fd1597d3141608c14f5f233e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "556e23ff88ad2fb8c44e393467cfba0c4f980cbe90316deaf48a1362f58cd118"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,018 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 15 KiB