2024-12-16 00:12:23 -06:00
|
|
|
module Config.Helpers.Markdown exposing (..)
|
|
|
|
|
|
|
|
import Browser
|
|
|
|
import Config.Helpers.Format
|
|
|
|
exposing
|
|
|
|
( headerFontSizeBig
|
|
|
|
, headerFontSizeMedium
|
|
|
|
, headerFontSizeSmall
|
|
|
|
, paragraphFontSize
|
|
|
|
)
|
|
|
|
import Config.Helpers.Response exposing (pageList)
|
|
|
|
import Config.Style.Colour exposing (colourTheme)
|
|
|
|
import Config.Style.Transitions
|
|
|
|
exposing
|
|
|
|
( hoverFontDarkOrange
|
|
|
|
, hoverFontLightOrange
|
|
|
|
, transitionStyleFast
|
|
|
|
, transitionStyleMedium
|
|
|
|
)
|
|
|
|
import Element as E exposing (..)
|
|
|
|
import Element.Background as B
|
|
|
|
import Element.Border as D
|
|
|
|
import Element.Font as F
|
|
|
|
import Element.Input as Input
|
|
|
|
import Element.Region as Region
|
|
|
|
import Html exposing (Attribute, Html)
|
|
|
|
import Html.Attributes
|
|
|
|
import Markdown.Block as Block exposing (Block, Inline, ListItem(..), Task(..))
|
|
|
|
import Markdown.Html
|
|
|
|
import Markdown.Parser
|
|
|
|
import Markdown.Renderer
|
|
|
|
|
|
|
|
|
|
|
|
articleImage : String -> Element msg
|
|
|
|
articleImage pic =
|
|
|
|
el
|
|
|
|
[ centerX
|
|
|
|
, width fill
|
|
|
|
]
|
|
|
|
<|
|
2024-12-16 01:57:02 -06:00
|
|
|
column
|
|
|
|
[ E.paddingEach
|
|
|
|
{ top = 20
|
|
|
|
, bottom = 20
|
|
|
|
, left = 20
|
|
|
|
, right = 20
|
|
|
|
}
|
|
|
|
]
|
|
|
|
[ image
|
|
|
|
[ width fill
|
|
|
|
]
|
|
|
|
{ src = "/blog/" ++ pic ++ ".png", description = "" }
|
2024-12-16 00:12:23 -06:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
renderDeviceMarkdown : String -> Element msg
|
|
|
|
renderDeviceMarkdown markdown =
|
|
|
|
case renderMarkdown markdown of
|
|
|
|
Ok renderedMarkdown ->
|
|
|
|
column
|
|
|
|
[ width fill
|
|
|
|
, centerX
|
2024-12-16 01:57:02 -06:00
|
|
|
, spacing 10
|
2024-12-16 00:12:23 -06:00
|
|
|
]
|
|
|
|
renderedMarkdown
|
|
|
|
|
|
|
|
Err error ->
|
|
|
|
E.text error
|
|
|
|
|
|
|
|
|
|
|
|
renderMarkdown : String -> Result String (List (Element msg))
|
|
|
|
renderMarkdown markdown =
|
|
|
|
markdown
|
|
|
|
|> Markdown.Parser.parse
|
|
|
|
|> Result.mapError (\error -> error |> List.map Markdown.Parser.deadEndToString |> String.join "\n")
|
|
|
|
|> Result.andThen (Markdown.Renderer.render elmUiRenderer)
|
|
|
|
|
|
|
|
|
|
|
|
elmUiRenderer : Markdown.Renderer.Renderer (Element msg)
|
|
|
|
elmUiRenderer =
|
|
|
|
{ heading = heading
|
|
|
|
, paragraph =
|
|
|
|
E.paragraph
|
|
|
|
[ E.spacing 3
|
|
|
|
, paragraphFontSize
|
|
|
|
, F.alignLeft
|
|
|
|
, width fill
|
|
|
|
]
|
|
|
|
, thematicBreak = E.none
|
|
|
|
, text = E.text
|
|
|
|
, strong =
|
|
|
|
\content ->
|
|
|
|
E.row
|
|
|
|
[ F.bold
|
|
|
|
]
|
|
|
|
content
|
|
|
|
, emphasis =
|
|
|
|
\content ->
|
|
|
|
E.row
|
|
|
|
[ F.italic
|
|
|
|
]
|
|
|
|
content
|
|
|
|
, strikethrough =
|
|
|
|
\content ->
|
|
|
|
E.row
|
|
|
|
[ F.strike
|
|
|
|
]
|
|
|
|
content
|
|
|
|
, codeSpan = code
|
|
|
|
, link =
|
|
|
|
\{ title, destination } body ->
|
|
|
|
E.newTabLink
|
|
|
|
[ width fill ]
|
|
|
|
{ url = destination
|
|
|
|
, label =
|
|
|
|
E.paragraph
|
|
|
|
[ F.color colourTheme.textLightOrange
|
|
|
|
, paragraphFontSize
|
|
|
|
, transitionStyleFast
|
|
|
|
, hoverFontDarkOrange
|
|
|
|
]
|
|
|
|
body
|
|
|
|
}
|
|
|
|
, hardLineBreak = Html.br [] [] |> E.html
|
|
|
|
, image =
|
|
|
|
\image ->
|
|
|
|
case image.title of
|
|
|
|
Just title ->
|
2024-12-16 01:29:31 -06:00
|
|
|
el [] <|
|
|
|
|
column
|
|
|
|
[ width fill
|
|
|
|
, E.centerX
|
|
|
|
]
|
|
|
|
[ E.image
|
|
|
|
[ E.centerX
|
|
|
|
, width <| px 600
|
|
|
|
]
|
|
|
|
{ src = image.src
|
|
|
|
, description = image.alt
|
|
|
|
}
|
|
|
|
]
|
2024-12-16 00:12:23 -06:00
|
|
|
|
|
|
|
Nothing ->
|
2024-12-16 01:57:02 -06:00
|
|
|
el
|
|
|
|
[ width fill ]
|
|
|
|
<|
|
2024-12-16 01:29:31 -06:00
|
|
|
column
|
2024-12-16 01:57:02 -06:00
|
|
|
[ E.centerX
|
|
|
|
, E.centerY
|
|
|
|
, width fill
|
|
|
|
, E.paddingEach
|
|
|
|
{ top = 0
|
|
|
|
, bottom = 10
|
|
|
|
, left = 10
|
|
|
|
, right = 10
|
|
|
|
}
|
2024-12-16 01:29:31 -06:00
|
|
|
]
|
|
|
|
[ E.image
|
|
|
|
[ E.centerX
|
2024-12-16 01:57:02 -06:00
|
|
|
, width (fill |> maximum 600)
|
2024-12-16 01:29:31 -06:00
|
|
|
]
|
|
|
|
{ src = image.src
|
|
|
|
, description = image.alt
|
|
|
|
}
|
2024-12-16 00:12:23 -06:00
|
|
|
]
|
|
|
|
, blockQuote =
|
|
|
|
\children ->
|
|
|
|
E.column
|
|
|
|
[ D.widthEach
|
|
|
|
{ top = 0
|
|
|
|
, right = 0
|
|
|
|
, bottom = 0
|
|
|
|
, left = 5
|
|
|
|
}
|
|
|
|
, D.rounded 10
|
|
|
|
, E.paddingEach
|
|
|
|
{ top = 13
|
|
|
|
, bottom = 10
|
|
|
|
, left = 10
|
|
|
|
, right = 10
|
|
|
|
}
|
|
|
|
, D.color colourTheme.textLightOrange
|
|
|
|
, B.color colourTheme.backgroundLightGrey
|
|
|
|
, paragraphFontSize
|
|
|
|
, width fill
|
|
|
|
]
|
|
|
|
children
|
|
|
|
, unorderedList =
|
|
|
|
\items ->
|
|
|
|
E.column
|
|
|
|
[ E.spacing 3
|
|
|
|
, paragraphFontSize
|
|
|
|
]
|
|
|
|
(items
|
|
|
|
|> List.map
|
|
|
|
(\(ListItem task children) ->
|
|
|
|
E.row
|
|
|
|
[ E.width fill
|
|
|
|
]
|
|
|
|
[ E.el
|
|
|
|
[ E.width <| E.px 20
|
|
|
|
, E.alignTop
|
|
|
|
, alignRight
|
|
|
|
]
|
|
|
|
(case task of
|
|
|
|
IncompleteTask ->
|
|
|
|
Input.defaultCheckbox False
|
|
|
|
|
|
|
|
CompletedTask ->
|
|
|
|
Input.defaultCheckbox True
|
|
|
|
|
|
|
|
NoTask ->
|
|
|
|
E.text "•"
|
|
|
|
)
|
|
|
|
, E.column
|
|
|
|
[ E.width fill, F.alignLeft ]
|
|
|
|
children
|
|
|
|
]
|
|
|
|
)
|
|
|
|
)
|
|
|
|
, orderedList =
|
|
|
|
\startingIndex items ->
|
|
|
|
E.column
|
|
|
|
[ E.spacing 3
|
|
|
|
, paragraphFontSize
|
|
|
|
, E.width fill
|
|
|
|
]
|
|
|
|
(items
|
|
|
|
|> List.indexedMap
|
|
|
|
(\index itemBlocks ->
|
|
|
|
E.row
|
|
|
|
[ E.width fill
|
|
|
|
]
|
|
|
|
[ E.el
|
|
|
|
[ E.alignTop
|
|
|
|
, width <| px 25
|
|
|
|
, F.bold
|
|
|
|
, alignRight
|
|
|
|
]
|
|
|
|
(E.text (String.fromInt (index + startingIndex) ++ "."))
|
|
|
|
, E.column
|
|
|
|
[ alignLeft
|
|
|
|
, E.width fill
|
|
|
|
]
|
|
|
|
itemBlocks
|
|
|
|
]
|
|
|
|
)
|
|
|
|
)
|
|
|
|
, codeBlock = codeBlock
|
|
|
|
, html = Markdown.Html.oneOf []
|
|
|
|
, table = E.column [ width fill ]
|
|
|
|
, tableHeader = E.column [ width fill ]
|
|
|
|
, tableBody = E.column [ width fill ]
|
|
|
|
, tableRow = E.row [ width fill ]
|
|
|
|
, tableHeaderCell =
|
|
|
|
\maybeAlignment children ->
|
|
|
|
E.paragraph [ width fill ] children
|
|
|
|
, tableCell =
|
|
|
|
\maybeAlignment children ->
|
|
|
|
E.paragraph [ width fill ] children
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
code : String -> Element msg
|
|
|
|
code snippet =
|
|
|
|
E.el
|
|
|
|
[ B.color colourTheme.backgroundLightGrey
|
|
|
|
, D.rounded 2
|
|
|
|
, E.paddingXY 5 3
|
|
|
|
, width fill
|
|
|
|
, F.family
|
|
|
|
[ F.external
|
|
|
|
{ url = "https://fonts.googleapis.com/css?family=Source+Code+Pro"
|
|
|
|
, name = "Source Code Pro"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]
|
|
|
|
(E.text snippet)
|
|
|
|
|
|
|
|
|
|
|
|
codeBlock : { body : String, language : Maybe String } -> Element msg
|
|
|
|
codeBlock details =
|
|
|
|
E.el
|
|
|
|
[ B.color colourTheme.backgroundLightGrey
|
|
|
|
, E.htmlAttribute (Html.Attributes.style "white-space" "pre")
|
|
|
|
, width fill
|
|
|
|
, E.paddingEach
|
|
|
|
{ top = 23
|
|
|
|
, bottom = 20
|
|
|
|
, left = 20
|
|
|
|
, right = 20
|
|
|
|
}
|
|
|
|
, paragraphFontSize
|
|
|
|
, F.family
|
|
|
|
[ F.external
|
|
|
|
{ url = "https://fonts.googleapis.com/css?family=Source+Code+Pro"
|
|
|
|
, name = "Source Code Pro"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]
|
|
|
|
(E.text details.body)
|
|
|
|
|
|
|
|
|
|
|
|
heading : { level : Block.HeadingLevel, rawText : String, children : List (Element msg) } -> Element msg
|
|
|
|
heading { level, rawText, children } =
|
|
|
|
E.paragraph
|
|
|
|
[ F.size
|
|
|
|
(case level of
|
|
|
|
Block.H1 ->
|
|
|
|
headerFontSizeBig
|
|
|
|
|
|
|
|
Block.H2 ->
|
|
|
|
headerFontSizeMedium
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
headerFontSizeSmall
|
|
|
|
)
|
|
|
|
, F.bold
|
|
|
|
, F.center
|
|
|
|
, width fill
|
|
|
|
, F.color colourTheme.textLightOrange
|
|
|
|
, Region.heading (Block.headingLevelToInt level)
|
|
|
|
, E.htmlAttribute
|
|
|
|
(Html.Attributes.attribute "name" (rawTextToId rawText))
|
|
|
|
, E.htmlAttribute
|
|
|
|
(Html.Attributes.id (rawTextToId rawText))
|
|
|
|
]
|
|
|
|
children
|
|
|
|
|
|
|
|
|
|
|
|
rawTextToId rawText =
|
|
|
|
rawText
|
|
|
|
|> String.split " "
|
|
|
|
|> Debug.log "split"
|
|
|
|
|> String.join "-"
|
|
|
|
|> Debug.log "joined"
|
|
|
|
|> String.toLower
|