use html generator for some things, fix x(x) styling

This commit is contained in:
FloatingGhost 2022-08-23 12:07:35 +01:00
parent 5054e0ba1e
commit 48d0da81e0
4 changed files with 117 additions and 149 deletions

View file

@ -1,6 +1,7 @@
defmodule MfmParser.Encoder do
alias MfmParser.Parser
alias MfmParser.Node
import Temple
@moduledoc """
An encoder who can turn a tree into HTML.
@ -27,191 +28,155 @@ defmodule MfmParser.Encoder do
"""
def to_html(tree) when is_list(tree) do
{html, _styles} = to_html_styles(tree)
html
to_html_styles(tree)
end
def to_html(input) when is_binary(input) do
Parser.parse(input) |> to_html()
end
defp to_html_styles(tree, _style \\ []) do
defp to_html_styles(tree) do
tree
|> Enum.reduce({"", []}, fn node, {html, styles} ->
case node do
%Node.Text{} ->
{html <> node.props.text, styles}
|> Enum.reduce("", fn node, html ->
html <>
case node do
%Node.Text{} ->
node.props.text
%Node.Newline{} ->
{html <> node.props.text, styles}
%Node.Newline{} ->
node.props.text
%Node.MFM.Flip{} ->
{html_child, styles_child} = to_html_styles(node.children)
%Node.MFM.Flip{} ->
html_child = to_html_styles(node.children)
case node.props do
%{v: true, h: true} ->
{html <>
"<span class=\"mfm\" style=\"display: inline-block; transform: scale(-1);\">#{html_child}</span>",
styles}
case node.props do
%{v: true, h: true} ->
temple do
span class: "mfm", style: "display: inline-block; transform: scale(-1);" do
html_child
end
end
%{v: true} ->
{html <>
"<span class=\"mfm\" style=\"display: inline-block; transform: scaleY(-1);\">#{html_child}</span>",
styles}
%{v: true} ->
"<span class=\"mfm\" style=\"display: inline-block; transform: scaleY(-1);\">#{html_child}</span>"
_ ->
{html <>
"<span class=\"mfm\" style=\"display: inline-block; transform: scaleX(-1);\">#{html_child}</span>",
styles ++ styles_child}
end
_ ->
"<span class=\"mfm\" style=\"display: inline-block; transform: scaleX(-1);\">#{html_child}</span>"
end
%Node.MFM.Font{} ->
{html_child, styles_child} = to_html_styles(node.children)
%Node.MFM.Font{} ->
html_child = to_html_styles(node.children)
{html <>
"<span class=\"mfm\" style=\"display: inline-block; font-family: #{node.props.font};\">#{html_child}</span>",
styles ++ styles_child}
temple do
span class: "mfm",
style: "display: inline-block; font-family: #{node.props.font};" do
html_child
end
end
%Node.MFM.X{} ->
{html_child, styles_child} = to_html_styles(node.children)
%Node.MFM.X{} ->
html_child = to_html_styles(node.children)
{html <>
"<span font-size: \"#{node.props.size}\">#{html_child}</span>",
styles ++ styles_child}
temple do
span style: "font-size: #{node.props.size}" do
html_child
end
end
%Node.MFM.Blur{} ->
{html_child, styles_child} = to_html_styles(node.children)
%Node.MFM.Blur{} ->
html_child = to_html_styles(node.children)
{html <> "<span class=\"_mfm_blur_\">#{html_child}</span>",
styles ++
[
"._mfm_blur_ { filter: blur(6px); transition: filter .3s; } ._mfm_blur_:hover { filter: blur(0px); }"
] ++ styles_child}
temple do
span(class: "mfm _mfm_blur_", do: html_child)
end
%Node.MFM.Jelly{} ->
{html_child, styles_child} = to_html_styles(node.children)
%Node.MFM.Jelly{} ->
html_child = to_html_styles(node.children)
{html <>
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite normal both running mfm-rubberBand;\">#{html_child}</span>",
styles ++
[
"@keyframes mfm-rubberBand { 0% { transform:scaleZ(1) } 30% { transform:scale3d(1.25,.75,1) } 40% { transform:scale3d(.75,1.25,1) } 50% { transform:scale3d(1.15,.85,1) } 65% { transform:scale3d(.95,1.05,1) } 75% { transform:scale3d(1.05,.95,1) } to { transform:scaleZ(1) }}"
] ++ styles_child}
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite normal both running mfm-rubberBand;\">#{html_child}</span>"
%Node.MFM.Tada{} ->
{html_child, styles_child} = to_html_styles(node.children)
%Node.MFM.Tada{} ->
html_child = to_html_styles(node.children)
{html <>
"<span class=\"mfm\" style=\"display: inline-block; font-size: 150%; animation: #{node.props.speed} linear 0s infinite normal both running mfm-tada;\">#{html_child}</span>",
styles ++
[
"@keyframes tada { 0% { transform: scaleZ(1); } 10%, 20% { transform: scale3d(.9,.9,.9) rotate3d(0,0,1,-3deg); } 30%, 50%, 70%, 90% { transform: scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg); } 40%, 60%, 80% { transform: scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg); } 100% { transform: scaleZ(1); }}"
] ++ styles_child}
temple do
span class: "mfm",
style:
"display: inline-block; font-size: 150%; animation: #{node.props.speed} linear 0s infinite normal both running mfm-tada;" do
html_child
end
end
%Node.MFM.Jump{} ->
{html_child, styles_child} = to_html_styles(node.children)
%Node.MFM.Jump{} ->
html_child = to_html_styles(node.children)
{html <>
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite normal none running mfm-jump;\">#{html_child}</span>",
styles ++
[
"@keyframes mfm-jump { 0% { transform:translateY(0) } 25% { transform:translateY(-16px) } 50% { transform:translateY(0) } 75% { transform:translateY(-8px) } to { transform:translateY(0) }}"
] ++ styles_child}
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite normal none running mfm-jump;\">#{html_child}</span>"
%Node.MFM.Bounce{} ->
{html_child, styles_child} = to_html_styles(node.children)
%Node.MFM.Bounce{} ->
html_child = to_html_styles(node.children)
{html <>
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite normal none running mfm-bounce; transform-origin: center bottom 0px;\">#{html_child}</span>",
styles ++
[
"@keyframes mfm-bounce { 0% { transform:translateY(0) scale(1) } 25% { transform:translateY(-16px) scale(1) } 50% { transform:translateY(0) scale(1) } 75% { transform:translateY(0) scale(1.5,.75) } to { transform:translateY(0) scale(1) }}"
] ++ styles_child}
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite normal none running mfm-bounce; transform-origin: center bottom 0px;\">#{html_child}</span>"
%Node.MFM.Spin{} ->
{html_child, styles_child} = to_html_styles(node.children)
%Node.MFM.Spin{} ->
html_child = to_html_styles(node.children)
styles_map = %{
"x" =>
"@keyframes mfm-spinX { 0% { transform:perspective(128px) rotateX(0) } to { transform:perspective(128px) rotateX(360deg) }}",
"y" =>
"@keyframes mfm-spinY { 0% { transform:perspective(128px) rotateY(0) } to { transform:perspective(128px) rotateY(360deg) }}",
"z" =>
"@keyframes mfm-spin { 0% { transform:rotate(0) } to { transform:rotate(360deg) }}"
}
keyframe_names_map = %{
"x" => "mfm-spinX",
"y" => "mfm-spinY",
"z" => "mfm-spin"
}
keyframe_names_map = %{
"x" => "mfm-spinX",
"y" => "mfm-spinY",
"z" => "mfm-spin"
}
directions_map = %{
"left" => "reverse"
}
directions_map = %{
"left" => "reverse"
}
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite #{Map.get(directions_map, node.props.direction, node.props.direction)} none running #{Map.get(keyframe_names_map, node.props.axis, "")};\">#{html_child}</span>"
{html <>
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite #{Map.get(directions_map, node.props.direction, node.props.direction)} none running #{Map.get(keyframe_names_map, node.props.axis, "")};\">#{html_child}</span>",
styles ++ [Map.get(styles_map, node.props.axis, "")] ++ styles_child}
%Node.MFM.Shake{} ->
html_child = to_html_styles(node.children)
%Node.MFM.Shake{} ->
{html_child, styles_child} = to_html_styles(node.children)
temple do
span class: "mfm",
style:
"display: inline-block; animation: #{node.props.speed} ease 0s infinite normal none running mfm-shake;" do
html_child
end
end
{html <>
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} ease 0s infinite normal none running mfm-shake;\">#{html_child}</span>",
styles ++
[
"@keyframes mfm-shake { 0% { transform:translate(-3px,-1px) rotate(-8deg) } 5% { transform:translateY(-1px) rotate(-10deg) } 10% { transform:translate(1px,-3px) rotate(0) } 15% { transform:translate(1px,1px) rotate(11deg) } 20% { transform:translate(-2px,1px) rotate(1deg) } 25% { transform:translate(-1px,-2px) rotate(-2deg) } 30% { transform:translate(-1px,2px) rotate(-3deg) } 35% { transform:translate(2px,1px) rotate(6deg) } 40% { transform:translate(-2px,-3px) rotate(-9deg) } 45% { transform:translateY(-1px) rotate(-12deg) } 50% { transform:translate(1px,2px) rotate(10deg) } 55% { transform:translateY(-3px) rotate(8deg) } 60% { transform:translate(1px,-1px) rotate(8deg) } 65% { transform:translateY(-1px) rotate(-7deg) } 70% { transform:translate(-1px,-3px) rotate(6deg) } 75% { transform:translateY(-2px) rotate(4deg) } 80% { transform:translate(-2px,-1px) rotate(3deg) } 85% { transform:translate(1px,-3px) rotate(-10deg) } 90% { transform:translate(1px) rotate(3deg) } 95% { transform:translate(-2px) rotate(-3deg) } to { transform:translate(2px,1px) rotate(2deg) }}"
] ++ styles_child}
%Node.MFM.Twitch{} ->
html_child = to_html_styles(node.children)
%Node.MFM.Twitch{} ->
{html_child, styles_child} = to_html_styles(node.children)
temple do
span class: "mfm",
style:
"display: inline-block; animation: #{node.props.speed} ease 0s infinite normal none running mfm-twitch;" do
html_child
end
end
{html <>
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} ease 0s infinite normal none running mfm-twitch;\">#{html_child}</span>",
styles ++
[
"@keyframes mfm-twitch { 0% { transform:translate(7px,-2px) } 5% { transform:translate(-3px,1px) } 10% { transform:translate(-7px,-1px) } 15% { transform:translateY(-1px) } 20% { transform:translate(-8px,6px) } 25% { transform:translate(-4px,-3px) } 30% { transform:translate(-4px,-6px) } 35% { transform:translate(-8px,-8px) } 40% { transform:translate(4px,6px) } 45% { transform:translate(-3px,1px) } 50% { transform:translate(2px,-10px) } 55% { transform:translate(-7px) } 60% { transform:translate(-2px,4px) } 65% { transform:translate(3px,-8px) } 70% { transform:translate(6px,7px) } 75% { transform:translate(-7px,-2px) } 80% { transform:translate(-7px,-8px) } 85% { transform:translate(9px,3px) } 90% { transform:translate(-3px,-2px) } 95% { transform:translate(-10px,2px) } to { transform:translate(-2px,-6px) }}"
] ++ styles_child}
%Node.MFM.Rainbow{} ->
html_child = to_html_styles(node.children)
%Node.MFM.Rainbow{} ->
{html_child, styles_child} = to_html_styles(node.children)
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite normal none running mfm-rainbow;\">#{html_child}</span>"
{html <>
"<span class=\"mfm\" style=\"display: inline-block; animation: #{node.props.speed} linear 0s infinite normal none running mfm-rainbow;\">#{html_child}</span>",
styles ++
[
"@keyframes mfm-rainbow { 0% { filter:hue-rotate(0deg) contrast(150%) saturate(150%) } to { filter:hue-rotate(360deg) contrast(150%) saturate(150%) }}"
] ++ styles_child}
%Node.MFM.Sparkle{} ->
# TODO: This is not how Misskey does it and should be changed to make it work like Misskey.
html_child = to_html_styles(node.children)
%Node.MFM.Sparkle{} ->
# TODO: This is not how Misskey does it and should be changed to make it work like Misskey.
{html_child, styles_child} = to_html_styles(node.children)
"<span class=\"mfm\" style=\"display: inline-block; animation: 1s linear 0s infinite normal none running mfm-sparkle;\">#{html_child}</span>"
{html <>
"<span class=\"mfm\" style=\"display: inline-block; animation: 1s linear 0s infinite normal none running mfm-sparkle;\">#{html_child}</span>",
styles ++
[
"@keyframes mfm-sparkle { 0% { filter: brightness(100%) } to { filter: brightness(300%) }}"
] ++ styles_child}
%Node.MFM.Rotate{} ->
html_child = to_html_styles(node.children)
%Node.MFM.Rotate{} ->
{html_child, styles_child} = to_html_styles(node.children)
"<span class=\"mfm\" style=\"display: inline-block; transform: rotate(90deg); transform-origin: center center 0px;\">#{html_child}</span>"
{html <>
"<span class=\"mfm\" style=\"display: inline-block; transform: rotate(90deg); transform-origin: center center 0px;\">#{html_child}</span>",
styles ++ styles_child}
%Node.MFM.Undefined{} ->
html_child = to_html_styles(node.children)
%Node.MFM.Undefined{} ->
{html_child, styles_child} = to_html_styles(node.children)
"<span>#{html_child}</span>"
{html <>
"<span>#{html_child}</span>", styles ++ styles_child}
_ ->
{html, styles}
end
_ ->
html
end
end)
end
end

View file

@ -4,7 +4,7 @@ defmodule MfmParser.MixProject do
def project do
[
app: :mfm_parser,
version: "0.1.0",
version: "0.1.1",
elixir: "~> 1.13",
start_permanent: Mix.env() == :prod,
deps: deps()
@ -21,8 +21,7 @@ defmodule MfmParser.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
{:temple, git: "git@akkoma.dev:floatingghost/temple.git", ref: "066a699ade472d8fa42a9d730b29a61af9bc8b59"}
]
end
end

4
mix.lock Normal file
View file

@ -0,0 +1,4 @@
%{
"phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"},
"temple": {:git, "git@akkoma.dev:floatingghost/temple.git", "066a699ade472d8fa42a9d730b29a61af9bc8b59", [ref: "066a699ade472d8fa42a9d730b29a61af9bc8b59"]},
}

View file

@ -80,7 +80,7 @@ defmodule MfmParser.EncoderTest do
}
]
expected = ~s[<span font-size: "400%">🍮</span>]
expected = ~s[<span style="font-size: 400%">🍮</span>]
assert Encoder.to_html(input_tree) == expected
end
@ -92,7 +92,7 @@ defmodule MfmParser.EncoderTest do
}
]
expected = ~s[<span class="_mfm_blur_">Misskey expands the world of the Fediverse</span>]
expected = ~s[<span class="mfm _mfm_blur_">Misskey expands the world of the Fediverse</span>]
assert Encoder.to_html(input_tree) == expected
end
@ -414,7 +414,7 @@ defmodule MfmParser.EncoderTest do
]
expected =
"It's not <span class=\"mfm\" style=\"display: inline-block; animation: 0.2s ease 0s infinite normal none running mfm-twitch;\">chocolatine</span>\nit's <span font-size: \"600%\"><span class=\"mfm\" style=\"display: inline-block; animation: 1s linear 0s infinite normal none running mfm-spin;\">pain</span> <span class=\"mfm\" style=\"display: inline-block; animation: 2s linear 0s infinite normal none running mfm-rainbow;\">au</span> <span class=\"mfm\" style=\"display: inline-block; animation: 0.5s linear 0s infinite normal none running mfm-jump;\">chocolat</span></span>"
"It's not <span class=\"mfm\" style=\"display: inline-block; animation: 0.2s ease 0s infinite normal none running mfm-twitch;\">chocolatine</span>\nit's <span style=\"font-size: 600%\"><span class=\"mfm\" style=\"display: inline-block; animation: 1s linear 0s infinite normal none running mfm-spin;\">pain</span> <span class=\"mfm\" style=\"display: inline-block; animation: 2s linear 0s infinite normal none running mfm-rainbow;\">au</span> <span class=\"mfm\" style=\"display: inline-block; animation: 0.5s linear 0s infinite normal none running mfm-jump;\">chocolat</span></span>"
assert Encoder.to_html(input_tree) == expected
end
@ -424,7 +424,7 @@ defmodule MfmParser.EncoderTest do
"It's not $[twitch.speed=0.2s chocolatine]\nit's $[x4 $[spin.speed=1s pain] $[rainbow.speed=2s au] $[jump.speed=0.5s chocolat]]"
expected =
"It's not <span class=\"mfm\" style=\"display: inline-block; animation: 0.2s ease 0s infinite normal none running mfm-twitch;\">chocolatine</span>\nit's <span font-size: \"600%\"><span class=\"mfm\" style=\"display: inline-block; animation: 1s linear 0s infinite normal none running mfm-spin;\">pain</span> <span class=\"mfm\" style=\"display: inline-block; animation: 2s linear 0s infinite normal none running mfm-rainbow;\">au</span> <span class=\"mfm\" style=\"display: inline-block; animation: 0.5s linear 0s infinite normal none running mfm-jump;\">chocolat</span></span>"
"It's not <span class=\"mfm\" style=\"display: inline-block; animation: 0.2s ease 0s infinite normal none running mfm-twitch;\">chocolatine</span>\nit's <span style=\"font-size: 600%\"><span class=\"mfm\" style=\"display: inline-block; animation: 1s linear 0s infinite normal none running mfm-spin;\">pain</span> <span class=\"mfm\" style=\"display: inline-block; animation: 2s linear 0s infinite normal none running mfm-rainbow;\">au</span> <span class=\"mfm\" style=\"display: inline-block; animation: 0.5s linear 0s infinite normal none running mfm-jump;\">chocolat</span></span>"
assert Encoder.to_html(input) == expected
end