From cb34575a43aba51b968f50c6d65e92ad0968cb90 Mon Sep 17 00:00:00 2001 From: Chris Done Date: Fri, 7 Mar 2014 23:37:04 +0100 Subject: [PATCH] Auto-reload support --- config/routes | 6 ++ hl.cabal | 6 +- src/Blaze/Prelude.hs | 10 ++- src/Blaze/Senza.hs | 67 +++++++++++++++++ src/DevelMain.hs | 16 +++-- src/HL/Controller/Community.hs | 10 +++ src/HL/Controller/Documentation.hs | 10 +++ src/HL/Controller/Downloads.hs | 10 +++ src/HL/Controller/News.hs | 10 +++ src/HL/Controller/Reload.hs | 14 ++-- src/HL/Controller/Theme.hs | 19 +++++ src/HL/Dispatch.hs | 6 ++ src/HL/Foundation.hs | 2 + src/HL/View/Community.hs | 32 +++++++++ src/HL/View/Documentation.hs | 25 +++++++ src/HL/View/Downloads.hs | 31 ++++++++ src/HL/View/Home.hs | 22 ++++-- src/HL/View/News.hs | 25 +++++++ src/HL/View/Template.hs | 112 +++++++++++++++++++---------- src/HL/View/Theme.hs | 94 ++++++++++++++++++++++++ src/Main.hs | 4 +- src/Yesod/Blaze.hs | 18 +++-- static/css/fonts/Haskell.dev.svg | 15 ++++ static/css/fonts/Haskell.eot | Bin 0 -> 1616 bytes static/css/fonts/Haskell.svg | 15 ++++ static/css/fonts/Haskell.ttf | Bin 0 -> 1452 bytes static/css/fonts/Haskell.woff | Bin 0 -> 1116 bytes static/css/haskell.font.css | 10 +++ static/js/warp.reload.js | 3 + 29 files changed, 530 insertions(+), 62 deletions(-) create mode 100644 src/Blaze/Senza.hs create mode 100644 src/HL/Controller/Community.hs create mode 100644 src/HL/Controller/Documentation.hs create mode 100644 src/HL/Controller/Downloads.hs create mode 100644 src/HL/Controller/News.hs create mode 100644 src/HL/Controller/Theme.hs create mode 100644 src/HL/View/Community.hs create mode 100644 src/HL/View/Documentation.hs create mode 100644 src/HL/View/Downloads.hs create mode 100644 src/HL/View/News.hs create mode 100644 src/HL/View/Theme.hs create mode 100644 static/css/fonts/Haskell.dev.svg create mode 100644 static/css/fonts/Haskell.eot create mode 100644 static/css/fonts/Haskell.svg create mode 100644 static/css/fonts/Haskell.ttf create mode 100644 static/css/fonts/Haskell.woff create mode 100644 static/css/haskell.font.css create mode 100644 static/js/warp.reload.js diff --git a/config/routes b/config/routes index 1f0c455..df56ea4 100644 --- a/config/routes +++ b/config/routes @@ -1,2 +1,8 @@ /static StaticR Static appStatic +/theme ThemeR GET / HomeR GET +/reload ReloadR GET +/downloads DownloadsR GET +/community CommunityR GET +/documentation DocumentationR GET +/news NewsR GET diff --git a/hl.cabal b/hl.cabal index eccbc4c..34dd936 100644 --- a/hl.cabal +++ b/hl.cabal @@ -15,7 +15,8 @@ executable hl hs-source-dirs: src/ ghc-options: -Wall -O2 main-is: Main.hs - build-depends: warp >= 2.0.3.2, + build-depends: conduit >= 1.0.15, + warp >= 2.0.3.2, wai-logger == 2.1.1, fast-logger == 2.1.5, yesod-core == 1.2.6.4, @@ -25,4 +26,5 @@ executable hl yesod-static == 1.2.2.1, base >= 4 && < 5, foreign-store == 0.0, - blaze == 0.0.2 + blaze == 0.0.2, + css == 0.1 diff --git a/src/Blaze/Prelude.hs b/src/Blaze/Prelude.hs index 346ba01..c7058ae 100644 --- a/src/Blaze/Prelude.hs +++ b/src/Blaze/Prelude.hs @@ -1,9 +1,13 @@ -- | A prelude for when using blaze-html. module Blaze.Prelude - (module Blaze - ,module Prelude) + (module Blaze.Attributes + ,module Blaze.Senza + ,module Prelude + ,docTypeHtml) where +import Blaze.Attributes hiding (style,span) +import Blaze.Senza import Blaze -import Prelude hiding (head,div,max,span,id) +import Prelude hiding (head,div,max,span,id,min) diff --git a/src/Blaze/Senza.hs b/src/Blaze/Senza.hs new file mode 100644 index 0000000..6f8324b --- /dev/null +++ b/src/Blaze/Senza.hs @@ -0,0 +1,67 @@ +-- | Blaze without attribute operators + +module Blaze.Senza where + +import Blaze (with,Html) +import qualified Blaze.Elements as E +import Prelude () + +meta :: [E.Attribute] -> Html +meta = with E.meta + +headtitle :: Html -> Html +headtitle = E.title + +style :: [E.Attribute] -> Html +style = with E.link + +script :: [E.Attribute] -> Html -> Html +script = with E.script + +div :: [E.Attribute] -> Html -> Html +div = with E.div + +span :: [E.Attribute] -> Html -> Html +span = with E.span + +link :: [E.Attribute] -> Html +link = with E.link + +a :: [E.Attribute] -> Html -> Html +a = with E.a + +h1 :: [E.Attribute] -> Html -> Html +h1 = with E.h1 + +h2 :: [E.Attribute] -> Html -> Html +h2 = with E.h2 + +h3 :: [E.Attribute] -> Html -> Html +h3 = with E.h3 + +h4 :: [E.Attribute] -> Html -> Html +h4 = with E.h4 + +h5 :: [E.Attribute] -> Html -> Html +h5 = with E.h5 + +head :: [E.Attribute] -> Html -> Html +head = with E.head + +body :: [E.Attribute] -> Html -> Html +body = with E.body + +nav :: [E.Attribute] -> Html -> Html +nav = with E.nav + +ul :: [E.Attribute] -> Html -> Html +ul = with E.ul + +ol :: [E.Attribute] -> Html -> Html +ol = with E.ol + +li :: [E.Attribute] -> Html -> Html +li = with E.li + +p :: [E.Attribute] -> Html -> Html +p = with E.p diff --git a/src/DevelMain.hs b/src/DevelMain.hs index 4c4be5c..70291f0 100644 --- a/src/DevelMain.hs +++ b/src/DevelMain.hs @@ -15,15 +15,19 @@ import Yesod.Static main :: IO (Store (IORef Application)) main = do s <- static "static" - app <- toWaiApp (App s) + c <- newChan + app <- toWaiApp (App s c) ref <- newIORef app tid <- forkIO (runSettings (defaultSettings { settingsPort = 1990 }) - (\req -> do handler <- readIORef ref - handler req)) + (\req -> + do handler <- readIORef ref + handler req)) _ <- newStore tid - newStore ref + ref <- newStore ref + newStore c + return ref -- | Update the server, start it if not running. update :: IO (Store (IORef Application)) @@ -33,7 +37,9 @@ update = Nothing -> main Just store -> do ref <- readStore store + c <- readStore (Store 2) + writeChan c () s <- static "static" - app <- toWaiApp (App s) + app <- toWaiApp (App s c) writeIORef ref app return store diff --git a/src/HL/Controller/Community.hs b/src/HL/Controller/Community.hs new file mode 100644 index 0000000..908fb0c --- /dev/null +++ b/src/HL/Controller/Community.hs @@ -0,0 +1,10 @@ +-- | Community page controller. + +module HL.Controller.Community where + +import HL.Foundation +import HL.View.Community + +-- | Community controller. +getCommunityR :: Handler Html +getCommunityR = blaze communityV diff --git a/src/HL/Controller/Documentation.hs b/src/HL/Controller/Documentation.hs new file mode 100644 index 0000000..ccc2f6b --- /dev/null +++ b/src/HL/Controller/Documentation.hs @@ -0,0 +1,10 @@ +-- | Documentation page controller. + +module HL.Controller.Documentation where + +import HL.Foundation +import HL.View.Documentation + +-- | Documentation controller. +getDocumentationR :: Handler Html +getDocumentationR = blaze documentationV diff --git a/src/HL/Controller/Downloads.hs b/src/HL/Controller/Downloads.hs new file mode 100644 index 0000000..fe708ab --- /dev/null +++ b/src/HL/Controller/Downloads.hs @@ -0,0 +1,10 @@ +-- | Downloads page controller. + +module HL.Controller.Downloads where + +import HL.Foundation +import HL.View.Downloads + +-- | Downloads controller. +getDownloadsR :: Handler Html +getDownloadsR = blaze downloadsV diff --git a/src/HL/Controller/News.hs b/src/HL/Controller/News.hs new file mode 100644 index 0000000..9616465 --- /dev/null +++ b/src/HL/Controller/News.hs @@ -0,0 +1,10 @@ +-- | News page controller. + +module HL.Controller.News where + +import HL.Foundation +import HL.View.News + +-- | News controller. +getNewsR :: Handler Html +getNewsR = blaze newsV diff --git a/src/HL/Controller/Reload.hs b/src/HL/Controller/Reload.hs index 916145e..7bd7a31 100644 --- a/src/HL/Controller/Reload.hs +++ b/src/HL/Controller/Reload.hs @@ -1,10 +1,14 @@ -- | Reload poller. -module HL.C.Reload where +module HL.Controller.Reload where import HL.Foundation -import HL.V.Home +import HL.View.Home --- | Home controller. -getHomeR :: Handler Html -getHomeR = +import Control.Concurrent.Chan.Lifted + +-- | Reload controller. +getReloadR :: Handler () +getReloadR = + do reload <- fmap appReload getYesod + dupChan reload >>= readChan diff --git a/src/HL/Controller/Theme.hs b/src/HL/Controller/Theme.hs new file mode 100644 index 0000000..65841b7 --- /dev/null +++ b/src/HL/Controller/Theme.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE OverloadedStrings #-} + +-- | CSS theme. + +module HL.Controller.Theme where + +import HL.Foundation +import HL.View.Theme + +import Data.Conduit +import Data.Conduit.List as CL +import Data.Text.Lazy (Text) +import Language.CSS + +-- | Generate CSS from Clay theme. +getThemeR :: Handler TypedContent +getThemeR = + respondSource "text/css" + (sendChunk (renderCSS (runCSS theme))) diff --git a/src/HL/Dispatch.hs b/src/HL/Dispatch.hs index 77eaae5..131b112 100644 --- a/src/HL/Dispatch.hs +++ b/src/HL/Dispatch.hs @@ -6,6 +6,12 @@ module HL.Dispatch () where import HL.Controller.Home +import HL.Controller.Reload +import HL.Controller.Theme +import HL.Controller.Downloads +import HL.Controller.Community +import HL.Controller.Documentation +import HL.Controller.News import HL.Foundation mkYesodDispatch "App" resourcesApp diff --git a/src/HL/Foundation.hs b/src/HL/Foundation.hs index 822041a..648e6b6 100644 --- a/src/HL/Foundation.hs +++ b/src/HL/Foundation.hs @@ -16,6 +16,7 @@ module HL.Foundation import HL.Static +import Control.Concurrent.Chan import Network.Wai.Logger import System.Log.FastLogger import Yesod @@ -26,6 +27,7 @@ import Yesod.Static -- | Application state. data App = App { appStatic :: Static + , appReload :: Chan () } -- | Generate boilerplate. diff --git a/src/HL/View/Community.hs b/src/HL/View/Community.hs new file mode 100644 index 0000000..145d11b --- /dev/null +++ b/src/HL/View/Community.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE NoImplicitPrelude #-} + +-- | Community page view. + +module HL.View.Community where + +import HL.Foundation +import HL.View.Template + +import Blaze.Prelude +import Blaze.Bootstrap + +-- | Community view. +communityV :: Blaze App +communityV = + template + [(CommunityR,"Community")] + (\_ -> + container + (row + (span12 + (do h1 [] "Community" + p [] + "The Haskell community is spread out online across several mediums \ + \and around the world!" + ul [] + (do li [] "The Haskell-Cafe mailing list" + li [] "StackOverflow" + li [] "G+" + li [] "Reddit" + li [] "The Wiki"))))) diff --git a/src/HL/View/Documentation.hs b/src/HL/View/Documentation.hs new file mode 100644 index 0000000..785fdd5 --- /dev/null +++ b/src/HL/View/Documentation.hs @@ -0,0 +1,25 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE NoImplicitPrelude #-} + +-- | Documentation page view. + +module HL.View.Documentation where + +import HL.Foundation +import HL.View.Template + +import Blaze.Prelude +import Blaze.Bootstrap + +-- | Documentation view. +documentationV :: Blaze App +documentationV = + template + [(DocumentationR,"Documentation")] + (\_ -> + container + (row + (span12 + (do h1 [] "Documentation" + h2 [] "Online Resources" + p [] "Some stuff here.")))) diff --git a/src/HL/View/Downloads.hs b/src/HL/View/Downloads.hs new file mode 100644 index 0000000..501cf8c --- /dev/null +++ b/src/HL/View/Downloads.hs @@ -0,0 +1,31 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE NoImplicitPrelude #-} + +-- | Downloads page view. + +module HL.View.Downloads where + +import HL.Foundation +import HL.View.Template + +import Blaze.Prelude +import Blaze.Bootstrap + +-- | Downloads view. +downloadsV :: Blaze App +downloadsV = + template + [(DownloadsR,"Downloads")] + (\_ -> + container + (row + (span12 + (do h1 [] "Downloads" + p [] + "The Haskell Platform was a comprehensive, robust development \ + \environment for programming in Haskell. For new users the \ + \platform makes it trivial to get up and running with a full \ + \Haskell development environment. For experienced developers, \ + \the platform provides a comprehensive, standard base for \ + \commercial and open source Haskell development that maximises \ + \interoperability and stability of your code.")))) diff --git a/src/HL/View/Home.hs b/src/HL/View/Home.hs index 736847d..b4f2b7b 100644 --- a/src/HL/View/Home.hs +++ b/src/HL/View/Home.hs @@ -8,17 +8,31 @@ module HL.View.Home where import HL.Foundation import HL.View.Template --- import Blaze.Elements as E --- import Blaze.Prelude +import Blaze.Prelude import Blaze.Bootstrap -- | Home view. homeV :: Blaze App homeV = template + [(HomeR,"Home")] (\_ -> container (row (span12 - (do h1 "hi" - with a [class_ "btn btn-primary"] "Hello?")))) + (do h1 [] "Haskell" + p [] + "The Haskell Platform was a comprehensive, robust development \ + \environment for programming in Haskell. For new users the \ + \platform makes it trivial to get up and running with a full \ + \Haskell development environment. For experienced developers, \ + \the platform provides a comprehensive, standard base for \ + \commercial and open source Haskell development that maximises \ + \interoperability and stability of your code." + p [] + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ + \Suspendisse vitae aliquet lorem. Praesent sed egestas risus. \ + \Cras a neque eget dui pharetra feugiat sed vel erat. Vivamus \ + \magna sapien, congue quis tellus eu, imperdiet sagittis dolor. \ + \Praesent dolor magna, suscipit in posuere nec, faucibus eu \ + \velit.")))) diff --git a/src/HL/View/News.hs b/src/HL/View/News.hs new file mode 100644 index 0000000..d9d4ae7 --- /dev/null +++ b/src/HL/View/News.hs @@ -0,0 +1,25 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE NoImplicitPrelude #-} + +-- | News page view. + +module HL.View.News where + +import HL.Foundation +import HL.View.Template + +import Blaze.Prelude +import Blaze.Bootstrap + +-- | News view. +newsV :: Blaze App +newsV = + template + [(NewsR,"News")] + (\_ -> + container + (row + (span12 + (do h1 [] "News" + p [] + "Insert news here.")))) diff --git a/src/HL/View/Template.hs b/src/HL/View/Template.hs index 1358be4..1b22776 100644 --- a/src/HL/View/Template.hs +++ b/src/HL/View/Template.hs @@ -5,49 +5,89 @@ module HL.View.Template where -import HL.Foundation +import HL.Foundation -import Blaze.Elements as E -import Blaze.Attributes as A -import Blaze.Prelude -import Blaze.Bootstrap +import Blaze (AttributeValue) +import qualified Blaze.Attributes as A +import Blaze.Bootstrap +import qualified Blaze.Elements as E +import Blaze.Prelude +import Blaze.Senza +import Control.Monad +import Data.Text (Text) -- | Render a template. -template :: Blaze App -- ^ Content. - -> Blaze App -template inner url = +template + :: [(Route App,Text)] + -> ((Route App -> AttributeValue) -> Html) + -> Blaze App +template crumbs inner cur url = docTypeHtml - (do head - (do E.title "Haskell" - with meta [charset "utf-8"] - with meta [httpEquiv "X-UA-Compatible",A.content "IE edge"] - with meta [name "viewport",A.content "width=device-width, initial-scale=1"] - styles [css_bootstrap_min_css - ,css_bootstrap_theme_min_css]) - body - (do with div - [class_ "wrap"] - (do inner url - with div - [class_ "footer"] - (container - (row - (span12 - (with div - [class_ "muted credit"] - "Footer"))))) - scripts [js_jquery_js - ,js_jquery_cookie_js - ,js_bootstrap_min_js])) + (do head [] + (do headtitle "Haskell" + meta [charset "utf-8"] + meta [httpEquiv "X-UA-Compatible",content "IE edge"] + meta [name "viewport",content "width=device-width, initial-scale=1"] + link [rel "stylesheet" + ,type_ "text/css" + ,href "http://fonts.googleapis.com/css?family=Open+Sans"] + styles [StaticR css_bootstrap_min_css + ,StaticR css_haskell_font_css + ,ThemeR]) + body [] + (do div [class_ "wrap"] + (do navigation cur url + container (bread url crumbs) + inner url) + div [class_ "footer"] + (div [class_ "container"] + (p [] (do "Copyright © 2014 haskell-lang.org"))) + scripts [js_jquery_js + ,js_jquery_cookie_js + ,js_bootstrap_min_js + ,js_warp_reload_js])) where scripts = mapM_ (\route -> - with script - [src (url (StaticR route))] - (return ())) + script [src (url (StaticR route))] + (return ())) styles = mapM_ (\route -> - with link - [rel "stylesheet" + link [rel "stylesheet" ,type_ "text/css" - ,href (url (StaticR route))]) + ,href (url route)]) + +-- | Main navigation. +navigation :: Blaze App +navigation cur url = + nav [class_ "navbar navbar-default"] + (div [class_ "container"] + (do brand + items)) + where items = + div [class_ "collapse navbar-collapse"] + (ul [class_ "nav navbar-nav"] + (mapM_ (uncurry item) + [(DownloadsR,"Downloads") + ,(CommunityR,"Community") + ,(DocumentationR,"Documentation") + ,(NewsR,"News")])) + where item route title = li theclass (a [href (url route)] title) + where theclass + | Just route == cur = [class_ "active"] + | otherwise = [] + brand = + div [class_ "navbar-header"] + (do a [class_ "navbar-brand" + ,href (url HomeR)] + (do span [class_ "logo"] + "\xe000" + "Haskell")) + +-- | Breadcrumb. +bread :: (t -> E.AttributeValue) -> [(t,Text)] -> Html +bread url crumbs = + ol [class_ "breadcrumb"] + (forM_ crumbs + (\(route,title) -> li [] (a [href (url route)] + (toHtml title)))) diff --git a/src/HL/View/Theme.hs b/src/HL/View/Theme.hs new file mode 100644 index 0000000..d6e1acc --- /dev/null +++ b/src/HL/View/Theme.hs @@ -0,0 +1,94 @@ +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE OverloadedStrings #-} + +-- | CSS theme. + +module HL.View.Theme + (theme) + where + +import Language.CSS + +theme = + do main + containers + breadcrumb + navbar + footer + +main = + do rule "html" + (do position "relative" + minHeight "100%") + rule "body" + (do background "#ffffff" + padding "0" + margin "0 0 4em 0" + fontFamily "Open Sans" + fontSize "13px") + rule ".wrap" + (do background "#ffffff" + paddingBottom "2em") + rule "p" + (do fontSize "15px") + rule "h1" + (do marginTop "0.1em" + marginLeft "0" + textIndent "-0.05em") + rule "h2" + (do color "#6e618d") + +containers = + rule ".container > .row" + (do marginLeft "0" + marginRight "0" + maxWidth "60em") + +navbar = + do rule ".navbar" + (do backgroundColor "#352f44" + borderRadius "0" + border "0" + marginBottom "0.5em") + rule ".navbar-header .navbar-brand" + (do color "#fff" + fontSize "inherit" + fontWeight "bold" + rule ".logo" + (do marginRight "0.5em" + fontFamily "haskell" + fontWeight "normal")) + rule ".navbar-header .navbar-brand:hover" + (color "#fff") + rule ".navbar-default .navbar-nav > .active > a" + (do theme + backgroundColor "#312b3f" + borderBottom "0.3em solid #465787") + rule ".navbar-default .navbar-nav > .active > a:hover" + (do theme + backgroundColor "#312b3f") + rule ".navbar-default .navbar-nav > li > a" + theme + where theme = + do color "#ffffff !important" + backgroundColor "inherit" + +breadcrumb = + rule ".breadcrumb" + (do marginLeft "0" + paddingLeft "0" + backgroundColor "inherit" + marginBottom "0") + +footer = + rule ".footer" + (do backgroundColor "#323232" + color "#999999" + position "absolute" + bottom "0" + width "100%" + height "4em" + lineHeight "2em" + rule "p" + (do marginTop "1em" + fontSize "13px")) diff --git a/src/Main.hs b/src/Main.hs index 39f515d..af43711 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -5,10 +5,12 @@ module Main where import HL.Foundation import HL.Dispatch () +import Control.Concurrent.Chan import Yesod.Static -- | Main entry point. main :: IO () main = do s <- static "static" - warp 1990 (App s) + c <- newChan + warp 1990 (App s c) diff --git a/src/Yesod/Blaze.hs b/src/Yesod/Blaze.hs index 3c94f70..7dfc177 100644 --- a/src/Yesod/Blaze.hs +++ b/src/Yesod/Blaze.hs @@ -2,17 +2,23 @@ module Yesod.Blaze (module Yesod.Blaze - ,module Yesod - ,module Blaze) + ,module Yesod) where import Yesod hiding (object) import Blaze -type Blaze a = (Route a -> AttributeValue) -> Html +-- | A blaze generator. +type Blaze a = + Maybe (Route a) -> + (Route a -> AttributeValue) -> + Html -- | Output some blaze, passes a URL renderer to the continuation. -blaze :: MonadHandler m => ((Route (HandlerSite m) -> AttributeValue) -> b) -> m b -blaze f = +blaze :: MonadHandler m => Blaze (HandlerSite m) -> m Html +blaze cont = do render <- getUrlRender - return (f (toValue . render)) + current <- getCurrentRoute + return + (cont current + (toValue . render)) diff --git a/static/css/fonts/Haskell.dev.svg b/static/css/fonts/Haskell.dev.svg new file mode 100644 index 0000000..2f40520 --- /dev/null +++ b/static/css/fonts/Haskell.dev.svg @@ -0,0 +1,15 @@ + + + + +This is a custom SVG font generated by IcoMoon. + + + + + + + + + + \ No newline at end of file diff --git a/static/css/fonts/Haskell.eot b/static/css/fonts/Haskell.eot new file mode 100644 index 0000000000000000000000000000000000000000..1ded7e4f304d77b6a5e6f61a726fd33196724651 GIT binary patch literal 1616 zcmds1OHUI~6#nkC0~io61%n2YfnsnWrH@2`5FrYM1q6c97&c}CLkpC)X~E#eG?9%P z!@@_1E?6qKkS%Ky!-f^?STr%ZGa(^tTfZ|iJPm)qGjr#B=ey5y&zUn?27J{41q~t^ zbdL?ltSCW4qmGSQSF;Cs!NU{2o@4G*S7Q(+a+rgKBp;v}W5jVR(lU`Ha~&}kLTCFlwESHzPqrUQw@Cg5qIy_~Sjsbc5s2jYFUdkGTW z-`L{ZI@&FX`GqC@B@xs&e(I9xNs|sf@>gq!+torRM7TRfo6ppCN9OdP%0px z(YGSBJ$ogYaxA*d=(x#QBC@cuxn40TO50T{&{3g=)HLk@S0CX9me1yb46c=rE2Go> zQ|K+=z&!)cX5{TN$pFU#f_1TI^Mn-1D`}1P8JJkKB(jb0+M%yF_Dy`u&Hr3U{_#4PC5(sM#WmHWF)37`XnW51yBVaTjM-WPAL`J+ zD5KuziTFHzwuth#miuqFM(X|Ts;S<;zMhwNVJ!r;r7kkNnaj}j{Y`eIO{4dE4?-erDT+P86 zaf6FJ=wsG6yqEl{i***4O&5!=dl$>mpqZPqlB`P?iqe=BUrd^;PEorgE!aBCvSq + + + +This is a custom SVG font generated by IcoMoon. + + + + + + + + + + \ No newline at end of file diff --git a/static/css/fonts/Haskell.ttf b/static/css/fonts/Haskell.ttf new file mode 100644 index 0000000000000000000000000000000000000000..be92b92fe53dbc55b8f8c34a3475862c60828f8f GIT binary patch literal 1452 zcmds1O-~b16g_X+ff^7nEe1_Z2CBh@lzt=%geXxcEFch+#<(#P7+RoTX^X*)X(Agp zhJ_y?x?riWku7Tz!-f^?STr%ZGa(^tThEI)yfRCd-x z3+^+%gSCm(hE39evO0(9r&$ez*~$_taM>d3i>(Ix^gCwXq`76wg~i#gt6$r?<2g_OyFa{F^ zEWtvGKa81$B`ZZRO(lV4Y9{ieR$@7262y>221VQFIl_X$FHDfL85m$YpRcAx(Sts= z2uFnxWs+>B7|Z4pmJtbti~+-$l_=WN*AoeaIp%z#)57AMGS8A1>_QACl*tjd@`Z(L z#s~*V{uTSzn_wTtnTKwljixl^YFy4N#6qEv_z`;P;d{qRL?pW|Uo?5Q6!+83r`u+t P$q4Me%7(?C{Z{=3#LT`m literal 0 HcmV?d00001 diff --git a/static/css/fonts/Haskell.woff b/static/css/fonts/Haskell.woff new file mode 100644 index 0000000000000000000000000000000000000000..f76118163463d0acb1d0b590fcd9176fd8e62764 GIT binary patch literal 1116 zcmXT-cXRU(3GruOV2NSiW&i><7Y0TUjR~CH+!PoX7&(C00f-xW=I@Mka|`ihU|{k9 zs+Iy`nbdPLdE8xG-GE{#K)x&xD=-)^u=oe-8v(^yfP8NtPSC$|S0p(%u>dG`0m!!k zniupy=+oJZ)I^{@W*(rJ9uS-Tv&fpB0Tcv^*#P-+K&-;@lwn6kZb=1DEC$F2spVn0 z!oZN5SOGMTIRnUN17qd@hP=ewRG=78oZ%kOj^+=}6Wj~(i%Wq14FKr_Vg&|f28N0` z$tekmi3uqwPd<3)obx+-639+UNZIgP@$?txzMt(Z-sb$p=KS3<;>{@=F8qI&XVKig z*ZOz*vwop4$+oIGNj5e{$u==ZNj5&oV_u4EViP1~c>eGt@oZXJIWv!qjm_bdsE1>2 zW8|>|2X36Zaqz&I6DQ7mj_Z8y&Ut#irv0)FpKE{r|L<7Bb-KRomv`me|B_)(&;B_7 zp?>9`|Nj>X8#k;`E&u;N|H53BX`H|R&sU6^DXOxS>qJkL0+-lZG1km`3?Y-Q&iXJv z@tmjlUkB#j zH^Tm;bC}wn|Mq|XQnS%KJ!x#-4kKheK`(OsJuhCMr0907)3LPA0cL(}BBKid?3&ABvXyMYvgiy+%IaM&iK zBqTgwPU2(}NJvUxaS}Mm^vn<#1qlqMzz6{8P1|+IqOoymWAnv!!`{ZmgR)-rsS{E% z5)**xQ_~ul8CfPPDx5WKJ@}CEFgx2Z_W$w*FAVJx0y2QMwrB@1IJq&zy77Dlo0X7| z){r2{=Ef%Qf$@{X!5T3(W`(niEj|jJ>^$-k|NgtDX&P`EIF&XsDoZmPmAf!8oQ&gG z2X`mKqbIhFH6Q<&#Y^s(DdF${rG183haCi5=jYs1RA~uOeELYM zw?)PL*jB-H!8^h?awIAqJasOR$8&C5$Z=pEiyX}#O{fbDyi9_>eM z?vH2wI9hS8%lvV~zsnT|W$z#J{uv+Y_Hpf*!(XzV%=~e);#}Vui)sH2;yxUi{xRrZ zyR9g*yUkj&-&q#F!aisP7f)Nw;Z<9kr|szJe>~HwS<9xcDdet9^wIzS|J|9@(z_@i zbY0J;_kOAJS?{|{ilftGU0