diff --git a/Render.hs b/Render.hs deleted file mode 100644 index d330b11..0000000 --- a/Render.hs +++ /dev/null @@ -1,203 +0,0 @@ --- \| Render.hs is a program to generate custom template: --- - projects list from directory listing --- - snippets from org mode file --- --- nix develop --command ghcid ./Render.hs -T main -{-# LANGUAGE BlockArguments #-} -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE ImportQualifiedPost #-} -{-# LANGUAGE OverloadedRecordDot #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} - -import Data.Aeson (FromJSON) -import Data.ByteString qualified as BS -import Data.ByteString.Char8 qualified as BS -import Data.List (isSuffixOf, sortOn) -import Data.String.QQ (s) -import Data.Text qualified as Text -import Data.Text.IO qualified as Text -import Data.Tree -import Data.Yaml hiding (Parser) -import Lucid -import Lucid.Base (makeAttribute, makeElement, makeElementNoEnd) -import RIO -import RIO.Text qualified -import System.Directory (listDirectory) -import System.FilePath -import Text.Pandoc.Class (runIOorExplode) -import Text.Pandoc.Definition -import Text.Pandoc.Extensions -import Text.Pandoc.Options (def, readerExtensions) -import Text.Pandoc.Readers.Markdown (readMarkdown) -import Text.Pandoc.Readers.Org (readOrg) -import Text.Parsec qualified as Parsec -import Text.Parsec.Text (Parser) - -header :: Text -header = - [s| ---- -title: Projects -pandoc: - rewriteClass: - plist: grid md:grid-cols-3 gap-4 mb-5 - pcard: rounded border-2 border-blue-100 p-1 - ptitle: text-lg - pdate: text-sm relative -top-3 -mb-3 - picon: relative -top-5 float right-0 ---- - - - -Here are some of the projects I have worked on, as an author or contributor. -|] - -data Project = Project - { fp :: FilePath - , meta :: ProjectMeta - , intro :: Text - } - -data ProjectMeta = ProjectMeta - { date :: Text - , tags :: [Text] - , title :: Text - } - deriving (Generic, FromJSON) - -parseProject :: FilePath -> IO Project -parseProject fp = do - putStrLn $ "[+] " <> fp - ("---" : lines) <- BS.lines <$> BS.readFile fp - let (yml, "---" : "" : intro : _) = break (== "---") lines - pm <- decodeThrow (BS.unlines yml) - pure $ Project fp pm (parseIntro $! decodeUtf8With lenientDecode intro) - -getIcon :: Project -> Maybe (Html ()) -getIcon p - | "fractal" `elem` p.meta.tags = Just "🥦" - | "design" `elem` p.meta.tags = Just "🎨" - | "video" `elem` p.meta.tags = Just "🎥" - | "library" `elem` p.meta.tags = Just "📖" - | "music" `elem` p.meta.tags = Just "🎵" - | "extension" `elem` p.meta.tags || "plugin" `elem` p.meta.tags = Just "⚙" - | "cli" `elem` p.meta.tags = Just cliSvg - | "web-service" `elem` p.meta.tags = Just srvSvg - | "nix" `elem` p.meta.tags || "packaging" `elem` p.meta.tags = Just "📦" - | "contributor" `elem` p.meta.tags = Just "🧑" - | "game" `elem` p.meta.tags = Just "🎮" - | "code" `elem` p.meta.tags || "keyboard" `elem` p.meta.tags = Just "⌨" - | otherwise = Nothing - -viewBox_ = makeAttribute "viewBox" -fill_ = makeAttribute "fill" -d_ = makeAttribute "d" -path_ = makeElement "path" - -srvSvg = - with svg_ [xmlns_ "http://www.w3.org/2000/svg", viewBox_ "0 0 24 24", width_ "24", height_ "24"] do - with path_ [fill_ "none", d_ "M0 0h24v24H0z"] mempty - with path_ [d_ "M5 11h14V5H5v6zm16-7v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1zm-2 9H5v6h14v-6zM7 15h3v2H7v-2zm0-8h3v2H7V7z"] mempty - -cliSvg = with svg_ [xmlns_ "http://www.w3.org/2000/svg", viewBox_ "0 0 24 24", width_ "24", height_ "24"] do - with path_ [fill_ "none", d_ "M0 0h24v24H0z"] mempty - with path_ [d_ "M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm9 12v2h6v-2h-6zm-3.586-3l-2.828 2.828L7 16.243 11.243 12 7 7.757 5.586 9.172 8.414 12z"] mempty - -renderProject :: Project -> Html () -renderProject p = - with div_ [class_ "rounded border-2 border-blue-100 p-1"] do - with div_ [class_ "text-lg"] do - let t = Text.pack (takeBaseName $ fp p) - with a_ [class_ "text-blue-600 mavenLinkBold hover:underline", href_ ("project/" <> t)] do - toHtml (if (Text.length p.meta.title < Text.length t) then p.meta.title else t) - - case getIcon p of - Just ico -> with span_ [class_ "float-right"] ico - Nothing -> pure () - - with span_ [class_ "text-sm relative -top-1"] (toHtml (p.meta.date)) - div_ (toHtml (intro p)) - -renderProjects :: [Project] -> Html () -renderProjects xs = with div_ [class_ "grid md:grid-cols-3 gap-4 mb-5"] do - traverse_ renderProject xs - -parseIntro :: Text -> Text -parseIntro t = case Parsec.runParser introParser () "" t of - Left e -> error (show t <> " -> " <> show e) - Right xs -> (mconcat . takeWhile (/= ".") $ xs) <> "." - -introParser :: Parser [Text] -introParser = Parsec.many1 (wordP <|> linkP <|> dotP) - where - dotP = "." <$ Parsec.char '.' - wordP = Text.pack <$> Parsec.many1 (Parsec.satisfy (`notElem` ['.', '#', '[', ']'])) - linkP = do - Parsec.optional (Parsec.char '#') - Parsec.many1 (Parsec.char '[') - l <- Text.pack <$> Parsec.many1 (Parsec.satisfy (`notElem` [']'])) - Parsec.many1 (Parsec.char ']') - Parsec.optional $ do - Parsec.char '(' - Parsec.many1 (Parsec.satisfy (`notElem` [')'])) - Parsec.char ')' - Parsec.optional (Parsec.char '#') - pure $ if Text.elem '|' l then Text.takeWhileEnd (/= '|') l else l - -formatIntro :: [Text] -> Text -formatIntro = flip mappend "." . mconcat . takeWhile (/= ".") - -mainProjs :: IO () -mainProjs = do - projFiles <- map (mappend "content/project/") <$> listDirectory "content/project" - projs <- traverse parseProject projFiles - renderToFile "content/templates/components/projects.tpl" (renderProjects (reverse $ sortOn (date . meta) projs)) - --- (id, title) -data DocHeading = DocHeading Text Text - -pandocTOCTree :: Pandoc -> Forest DocHeading -pandocTOCTree (Pandoc _ blocks) = go [] 1 blocks - where - go acc _lvl [] = reverse acc - go acc lvl (x : rest) = case x of - Header hlvl (oid, _, _) [Str title] - | hlvl == lvl -> - -- TODO: support nested heading - let oh = DocHeading oid title - childs = [] - in go (Node oh childs : acc) lvl rest - _ -> go acc lvl rest - -renderTOCTree :: Text -> Tree DocHeading -> Html () -renderTOCTree base (Node (DocHeading oid title) childs) = li_ do - with a_ [href_ (mconcat [base, "#", oid])] do - (toHtml title) - unless (null childs) do - ul_ do - traverse_ (renderTOCTree base) childs - -doRead :: FilePath -> IO Pandoc -doRead fp = do - content <- Text.readFile fp - runIOorExplode $ - if ".org" `isSuffixOf` fp - then readOrg readerOpts content - else readMarkdown readerOpts content - where - readerOpts = def{readerExtensions = extensionsFromList (exts)} - exts = [Ext_auto_identifiers] - -renderTOCTemplate :: FilePath -> Pandoc -> IO () -renderTOCTemplate fp doc = do - renderToFile fp do - ul_ do - traverse_ (renderTOCTree "snippets") (pandocTOCTree doc) - -mainSnippets :: IO () -mainSnippets = do - renderTOCTemplate "content/templates/components/toc-snippets.tpl" =<< doRead "content/snippets.org" - -main :: IO () -main = mainProjs >> mainSnippets diff --git a/Tutorials/Slug.md b/Tutorials/Slug.md new file mode 100644 index 0000000..1696f72 --- /dev/null +++ b/Tutorials/Slug.md @@ -0,0 +1,24 @@ +--- +page: + headHtml: | + +--- +By default the filesystem path is used to determine the note URL. You can override this using the `slug` frontmatter metadata. + +## Overriding the URL + +For example, + +- Source: https://raw.githubusercontent.com/srid/srid/master/free/Actualism%20Method.md +- Rendered: https://srid.ca/method + +Notice that in the source, we see: + +```yaml +--- +slug: method +--- +``` + +This makes Emantoe use `/method` as the URL path for this note. Without this, by default, Emanote would use `/free/Actualism%20Method`. + diff --git a/ema-template.cabal b/ema-template.cabal deleted file mode 100644 index dc20b7d..0000000 --- a/ema-template.cabal +++ /dev/null @@ -1,106 +0,0 @@ -cabal-version: 2.4 -name: ema-template -version: 0.1.0.0 -license: AGPL-3.0-only -copyright: 2022 Sridhar Ratnakumar -maintainer: srid@srid.ca -author: Sridhar Ratnakumar -category: Web - --- A short (one-line) description of the package. --- synopsis: - --- A longer description of the package. --- description: - --- A URL where users can report bugs. --- bug-reports: - -extra-source-files: - LICENSE - README.md - -data-dir: static -data-files: - *.css - *.svg - -executable ema-template - build-depends: - , aeson - , async - , base >=4 && <5 - , blaze-html - , blaze-markup - , containers - , data-default - , directory - , ema >=0.10 - , ema-extra >=0.10 - , ema-generics >=0.10 - , filepath - , generic-optics - , monad-logger - , optics-core - , optparse-applicative - , relude >=1.0 - , text - , time - , unionmount >=0.2 - , unliftio - - mixins: - base hiding (Prelude), - relude (Relude as Prelude, Relude.Container.One), - relude - - ghc-options: - -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns - -Wmissing-deriving-strategies -Wunused-foralls -Wunused-foralls - -fprint-explicit-foralls -fprint-explicit-kinds -threaded - - default-extensions: - BangPatterns - ConstraintKinds - DataKinds - DeriveAnyClass - DeriveDataTypeable - DeriveFoldable - DeriveFunctor - DeriveGeneric - DeriveLift - DeriveTraversable - DerivingStrategies - DerivingVia - EmptyCase - EmptyDataDecls - EmptyDataDeriving - ExistentialQuantification - ExplicitForAll - FlexibleContexts - FlexibleInstances - GADTSyntax - GeneralisedNewtypeDeriving - ImportQualifiedPost - KindSignatures - LambdaCase - MultiParamTypeClasses - MultiWayIf - NoStarIsType - NumericUnderscores - OverloadedStrings - PolyKinds - PostfixOperators - RankNTypes - ScopedTypeVariables - StandaloneDeriving - StandaloneKindSignatures - TupleSections - TypeApplications - TypeFamilies - TypeOperators - ViewPatterns - - main-is: Main.hs - hs-source-dirs: src - default-language: Haskell2010 diff --git a/flake.lock b/flake.lock index 6712526..0f63492 100644 --- a/flake.lock +++ b/flake.lock @@ -154,7 +154,7 @@ "commonmark-simple": "commonmark-simple", "commonmark-wikilink": "commonmark-wikilink", "ema": "ema", - "emanote-template": "emanote-template_2", + "emanote-template": [], "flake-parts": "flake-parts_2", "flake-root": "flake-root_2", "haskell-flake": "haskell-flake_2", @@ -166,17 +166,16 @@ "unionmount": "unionmount_2" }, "locked": { - "lastModified": 1719828900, - "narHash": "sha256-t4Bp56TAZwOwAS6ezev+wE3qtcpBCOkHVNnpbaG47JE=", + "lastModified": 1719887178, + "narHash": "sha256-GAxmOxH0NnzgIjtcY5OvLguXlQuh2Hs82pQ8WLBai04=", "owner": "srid", "repo": "emanote", - "rev": "32666807bc2966c796eb95364716dfb08c50cefe", + "rev": "e20dbc918df86436ad1e97c79b74337b3625798f", "type": "github" }, "original": { "owner": "srid", "repo": "emanote", - "rev": "32666807bc2966c796eb95364716dfb08c50cefe", "type": "github" } }, @@ -196,22 +195,6 @@ "type": "github" } }, - "emanote-template_2": { - "flake": false, - "locked": { - "lastModified": 1720320666, - "narHash": "sha256-Q6jBdnWi/K6Zlv8igHy8DFi0gHE6/aNWABChvBzqVJk=", - "owner": "srid", - "repo": "emanote-template", - "rev": "4441176de6094af603e75dfffb2ecc56cb2f33b9", - "type": "github" - }, - "original": { - "owner": "srid", - "repo": "emanote-template", - "type": "github" - } - }, "emanote_2": { "inputs": { "commonmark-simple": "commonmark-simple_2", @@ -454,26 +437,17 @@ "type": "github" } }, - "nixpkgs_3": { - "locked": { - "lastModified": 1672755833, - "narHash": "sha256-AmPzlvY1Y5codnytZCDn2wlhVa8rDHNib5wBWtcD7c4=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "3665c429d349fbda46b0651e554cca8434452748", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "3665c429d349fbda46b0651e554cca8434452748", - "type": "github" - } - }, "root": { "inputs": { "emanote": "emanote", - "nixpkgs": "nixpkgs_3" + "flake-parts": [ + "emanote", + "flake-parts" + ], + "nixpkgs": [ + "emanote", + "nixpkgs" + ] } }, "systems": { diff --git a/flake.nix b/flake.nix index f1706a1..33ba1b9 100644 --- a/flake.nix +++ b/flake.nix @@ -1,68 +1,37 @@ { - inputs = { - emanote.url = "github:srid/emanote/32666807bc2966c796eb95364716dfb08c50cefe"; - nixpkgs.url = "github:NixOS/nixpkgs/3665c429d349fbda46b0651e554cca8434452748"; + nixConfig = { + extra-substituters = "https://cache.garnix.io"; + extra-trusted-public-keys = "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="; }; - outputs = inputs: let - emanote = inputs.emanote.packages.x86_64-linux.default; - pkgs = import inputs.nixpkgs {system = "x86_64-linux";}; - ebml = pkgs.fetchFromGitHub { - owner = "TristanCacqueray"; - repo = "haskell-ebml"; - rev = "aff25512b52e48e92d77cd59019a0291a8b43bf4"; - sha256 = "sha256-U2Mo83gr7dLm+rRKOLzS9LZUaZ90ECO6Zjbv6maflyc="; - }; - ghc = pkgs.haskellPackages.ghcWithPackages (p: [ - p.markdown-unlit - p.rio - p.string-qq - p.ki - p.servant - p.servant-websockets - p.servant - p.lucid - p.servant-lucid - p.websockets - p.yaml - p.pandoc - p.pandoc-types - (pkgs.haskellPackages.callCabal2nix "ebml" ebml {}) - ]); - website = pkgs.stdenv.mkDerivation { - name = "felipemarcelino.io-pages"; - buildInputs = [emanote]; - src = inputs.self; - # https://github.com/jaspervdj/hakyll/issues/614 - # https://github.com/NixOS/nix/issues/318#issuecomment-52986702 - # https://github.com/MaxDaten/brutal-recipes/blob/source/default.nix#L24 - LOCALE_ARCHIVE = - pkgs.lib.optionalString (pkgs.buildPlatform.libc == "glibc") - "${pkgs.glibcLocales}/lib/locale/locale-archive"; - LANG = "en_US.UTF-8"; + inputs = { + emanote.url = "github:srid/emanote"; + emanote.inputs.emanote-template.follows = ""; + nixpkgs.follows = "emanote/nixpkgs"; + flake-parts.follows = "emanote/flake-parts"; + }; - buildPhase = '' - mkdir _out - emanote -L content/ gen _out - ''; - installPhase = '' - mv _out $out - ''; + outputs = inputs@{ self, flake-parts, nixpkgs, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + systems = nixpkgs.lib.systems.flakeExposed; + imports = [ inputs.emanote.flakeModule ]; + perSystem = { self', pkgs, system, ... }: { + emanote = { + # By default, the 'emanote' flake input is used. + # package = inputs.emanote.packages.${system}.default; + sites."default" = { + layers = [{ path = ./.; pathString = "."; }]; + # port = 8080; + baseUrl = "/emanote-template/"; # Change to "/" (or remove it entirely) if using CNAME + # prettyUrls = true; + }; + }; + devShells.default = pkgs.mkShell { + buildInputs = [ + pkgs.nixpkgs-fmt + ]; + }; + formatter = pkgs.nixpkgs-fmt; + }; }; - run = pkgs.writeScriptBin "run" '' - ${emanote}/bin/emanote -L content/ run --host 0.0.0.0 --port 8080 - ''; - in { - packages.x86_64-linux.default = website; - apps."x86_64-linux".default = { - type = "app"; - program = "${run}/bin/run"; - }; - devShells."x86_64-linux".default = - pkgs.mkShell {buildInputs = [ghc pkgs.ghcid emanote];}; - devShells."x86_64-linux".gstreamer = pkgs.mkShell { - buildInputs = [ghc pkgs.ghcid pkgs.gst_all_1.gstreamer]; - GST_PLUGIN_PATH = "${pkgs.gst_all_1.gst-plugins-base}/lib/gstreamer-1.0/:${pkgs.gst_all_1.gst-plugins-good}/lib/gstreamer-1.0/"; - }; - }; } diff --git a/index.yaml b/index.yaml index f48f69b..b3d3767 100644 --- a/index.yaml +++ b/index.yaml @@ -19,6 +19,7 @@ template: theme: red #iconUrl: static/favicon.jpeg urlStrategy: pretty + editBaseUrl: https://github.com/srid/emanote-template/edit/master pandoc: rewriteClass: prose: max-w-prose mx-auto diff --git a/justfile b/justfile deleted file mode 100644 index 4a39b3f..0000000 --- a/justfile +++ /dev/null @@ -1,19 +0,0 @@ -default: - @just --list - -# Run hoogle -docs: - echo http://127.0.0.1:8888 - hoogle serve -p 8888 --local - -# Run cabal repl -repl *ARGS: - cabal repl {{ARGS}} - -# Autoformat the project tree -fmt: - treefmt - -# Run the dev server (ghcid + tailwind) -run: - ema-tailwind-run diff --git a/src/Main.hs b/src/Main.hs deleted file mode 100644 index 4c918a9..0000000 --- a/src/Main.hs +++ /dev/null @@ -1,158 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE UndecidableInstances #-} - -module Main where - -import Data.Default (def) -import Data.Generics.Sum.Any (AsAny (_As)) -import Ema -import Ema.CLI qualified -import Ema.Route.Generic.TH -import Ema.Route.Lib.Extra.StaticRoute qualified as SR -import Optics.Core (Prism', (%)) -import Options.Applicative -import Text.Blaze.Html.Renderer.Utf8 qualified as RU -import Text.Blaze.Html5 ((!)) -import Text.Blaze.Html5 qualified as H -import Text.Blaze.Html5.Attributes qualified as A - -data Model = Model - { modelBaseUrl :: Text - , modelStatic :: SR.Model - } - deriving stock (Eq, Show, Generic) - -data HtmlRoute - = HtmlRoute_Index - | HtmlRoute_About - deriving stock (Show, Eq, Ord, Generic, Enum, Bounded) - -deriveGeneric ''HtmlRoute -deriveIsRoute ''HtmlRoute [t|'[]|] - -type StaticRoute = SR.StaticRoute "static" - -data Route - = Route_Html HtmlRoute - | Route_Static StaticRoute - deriving stock (Eq, Show, Ord, Generic) - -deriveGeneric ''Route -deriveIsRoute - ''Route - [t| - [ -- To render a `Route` we need `Model` - WithModel Model - , -- Override default sub-route encoding (to avoid the folder prefix in encoded URLs) - WithSubRoutes [HtmlRoute, StaticRoute] - ] - |] - -instance EmaSite Route where - type SiteArg Route = CliArgs - siteInput cliAct args = do - staticRouteDyn <- siteInput @StaticRoute cliAct () - pure $ Model (cliArgsBaseUrl args) <$> staticRouteDyn - siteOutput rp m = \case - Route_Html r -> - pure $ Ema.AssetGenerated Ema.Html $ renderHtmlRoute rp m r - Route_Static r -> - siteOutput (rp % (_As @"Route_Static")) (modelStatic m) r - -renderHtmlRoute :: Prism' FilePath Route -> Model -> HtmlRoute -> LByteString -renderHtmlRoute rp m r = do - RU.renderHtml $ do - H.docType - H.html ! A.lang "en" $ do - H.head $ do - renderHead rp m r - H.body ! A.class_ "bg-gray-50" $ do - renderBody rp m r - -renderHead :: Prism' FilePath Route -> Model -> HtmlRoute -> H.Html -renderHead rp model r = do - H.meta ! A.charset "UTF-8" - H.meta ! A.name "viewport" ! A.content "width=device-width, initial-scale=1" - H.title $ H.toHtml $ routeTitle r <> " - Ema Template" - H.base ! A.href (H.toValue $ modelBaseUrl model) - H.link ! A.rel "stylesheet" ! A.href (staticRouteUrl rp model "tailwind.css") - -renderBody :: Prism' FilePath Route -> Model -> HtmlRoute -> H.Html -renderBody rp model r = do - H.div ! A.class_ "container mx-auto mt-8 p-4 max-w-prose border-2 bg-white rounded-lg shadow" $ do - renderNavbar rp r - H.h1 ! A.class_ "text-3xl font-bold" $ H.toHtml $ routeTitle r - case r of - HtmlRoute_Index -> do - "You are on the index page. Want to see " - routeLink rp HtmlRoute_About "About" - "?" - HtmlRoute_About -> do - "You are on the about page." - H.a ! A.href (staticRouteUrl rp model "logo.svg") ! A.target "_blank" $ do - H.img ! A.src (staticRouteUrl rp model "logo.svg") ! A.class_ "py-4 w-32" ! A.alt "Ema Logo" - -renderNavbar :: Prism' FilePath Route -> HtmlRoute -> H.Html -renderNavbar rp currentRoute = - H.nav ! A.class_ "w-full text-xl font-bold flex space-x-4 mb-4" $ do - forM_ (universe @HtmlRoute) $ \r -> - let extraClass = if r == currentRoute then "bg-rose-400 text-white" else "text-gray-700" - in H.a - ! A.href (H.toValue $ routeUrl rp $ Route_Html r) - ! A.class_ ("rounded p-2 " <> extraClass) - $ H.toHtml - $ routeTitle r - -routeTitle :: HtmlRoute -> Text -routeTitle r = case r of - HtmlRoute_Index -> "Home" - HtmlRoute_About -> "About" - -routeLink :: Prism' FilePath Route -> HtmlRoute -> H.Html -> H.Html -routeLink rp r = - H.a - ! A.href (H.toValue $ routeUrl rp $ Route_Html r) - ! A.class_ "text-rose-400" - --- | Link to a file under ./static -staticRouteUrl :: (IsString r) => Prism' FilePath Route -> Model -> FilePath -> r -staticRouteUrl rp m = - SR.staticRouteUrl (rp % (_As @"Route_Static")) (modelStatic m) - --- CLI argument handling --- --------------------- - -data CliArgs = CliArgs - { cliArgsBaseUrl :: Text - , cliArgsEmaCli :: Ema.CLI.Cli - } - deriving stock (Eq, Show) - -parseCliArgs :: IO CliArgs -parseCliArgs = - execParser $ parserInfo cliParser - where - cliParser :: Parser CliArgs - cliParser = - CliArgs - <$> option str (long "base-url" <> metavar "BASE_URL" <> help "Base URL to use in " <> value "/") - <*> Ema.CLI.cliParser - parserInfo :: Parser a -> ParserInfo a - parserInfo p = - info - (versionOption <*> p <**> helper) - ( fullDesc - <> progDesc "ema-template: TODO" - <> header "ema-template" - ) - where - versionOption = infoOption "0.1" (long "version" <> help "Show version") - --- Main entrypoint --- --------------- - -main :: IO () -main = do - cliArgs <- parseCliArgs - let cfg = SiteConfig (cliArgsEmaCli cliArgs) def - void $ Ema.runSiteWith @Route cfg cliArgs diff --git a/static/logo.svg b/static/logo.svg deleted file mode 100644 index fcfc3c7..0000000 --- a/static/logo.svg +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/static/tailwind.css b/static/tailwind.css deleted file mode 100644 index 5782005..0000000 --- a/static/tailwind.css +++ /dev/null @@ -1,898 +0,0 @@ -/* -! tailwindcss v3.4.6 | MIT License | https://tailwindcss.com -*/ - -/* -1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) -2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) -*/ - -*, -::before, -::after { - box-sizing: border-box; - /* 1 */ - border-width: 0; - /* 2 */ - border-style: solid; - /* 2 */ - border-color: #e5e7eb; - /* 2 */ -} - -::before, -::after { - --tw-content: ''; -} - -/* -1. Use a consistent sensible line-height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -3. Use a more readable tab size. -4. Use the user's configured `sans` font-family by default. -5. Use the user's configured `sans` font-feature-settings by default. -6. Use the user's configured `sans` font-variation-settings by default. -7. Disable tap highlights on iOS -*/ - -html, -:host { - line-height: 1.5; - /* 1 */ - -webkit-text-size-adjust: 100%; - /* 2 */ - -moz-tab-size: 4; - /* 3 */ - -o-tab-size: 4; - tab-size: 4; - /* 3 */ - font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - /* 4 */ - font-feature-settings: normal; - /* 5 */ - font-variation-settings: normal; - /* 6 */ - -webkit-tap-highlight-color: transparent; - /* 7 */ -} - -/* -1. Remove the margin in all browsers. -2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. -*/ - -body { - margin: 0; - /* 1 */ - line-height: inherit; - /* 2 */ -} - -/* -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -3. Ensure horizontal rules are visible by default. -*/ - -hr { - height: 0; - /* 1 */ - color: inherit; - /* 2 */ - border-top-width: 1px; - /* 3 */ -} - -/* -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/* -Remove the default font size and weight for headings. -*/ - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/* -Reset links to optimize for opt-in styling instead of opt-out. -*/ - -a { - color: inherit; - text-decoration: inherit; -} - -/* -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/* -1. Use the user's configured `mono` font-family by default. -2. Use the user's configured `mono` font-feature-settings by default. -3. Use the user's configured `mono` font-variation-settings by default. -4. Correct the odd `em` font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - /* 1 */ - font-feature-settings: normal; - /* 2 */ - font-variation-settings: normal; - /* 3 */ - font-size: 1em; - /* 4 */ -} - -/* -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/* -Prevent `sub` and `sup` elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -3. Remove gaps between table borders by default. -*/ - -table { - text-indent: 0; - /* 1 */ - border-color: inherit; - /* 2 */ - border-collapse: collapse; - /* 3 */ -} - -/* -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -3. Remove default padding in all browsers. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; - /* 1 */ - font-feature-settings: inherit; - /* 1 */ - font-variation-settings: inherit; - /* 1 */ - font-size: 100%; - /* 1 */ - font-weight: inherit; - /* 1 */ - line-height: inherit; - /* 1 */ - letter-spacing: inherit; - /* 1 */ - color: inherit; - /* 1 */ - margin: 0; - /* 2 */ - padding: 0; - /* 3 */ -} - -/* -Remove the inheritance of text transform in Edge and Firefox. -*/ - -button, -select { - text-transform: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Remove default button styles. -*/ - -button, -input:where([type='button']), -input:where([type='reset']), -input:where([type='submit']) { - -webkit-appearance: button; - /* 1 */ - background-color: transparent; - /* 2 */ - background-image: none; - /* 2 */ -} - -/* -Use the modern Firefox focus style for all focusable elements. -*/ - -:-moz-focusring { - outline: auto; -} - -/* -Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/* -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/* -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/* -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ -} - -/* -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to `inherit` in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ -} - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/* -Removes the default spacing and border for appropriate elements. -*/ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -fieldset { - margin: 0; - padding: 0; -} - -legend { - padding: 0; -} - -ol, -ul, -menu { - list-style: none; - margin: 0; - padding: 0; -} - -/* -Reset default styling for dialogs. -*/ - -dialog { - padding: 0; -} - -/* -Prevent resizing textareas horizontally by default. -*/ - -textarea { - resize: vertical; -} - -/* -1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) -2. Set the default placeholder color to the user's configured gray 400 color. -*/ - -input::-moz-placeholder, textarea::-moz-placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - /* 1 */ - color: #9ca3af; - /* 2 */ -} - -/* -Set the default cursor for buttons. -*/ - -button, -[role="button"] { - cursor: pointer; -} - -/* -Make sure disabled buttons don't get the pointer cursor. -*/ - -:disabled { - cursor: default; -} - -/* -1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) -2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) - This can trigger a poorly considered lint error in some tools but is included by design. -*/ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; - /* 1 */ - vertical-align: middle; - /* 2 */ -} - -/* -Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) -*/ - -img, -video { - max-width: 100%; - height: auto; -} - -/* Make elements with the HTML hidden attribute stay hidden by default */ - -[hidden] { - display: none; -} - -[type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; - border-radius: 0px; - padding-top: 0.5rem; - padding-right: 0.75rem; - padding-bottom: 0.5rem; - padding-left: 0.75rem; - font-size: 1rem; - line-height: 1.5rem; - --tw-shadow: 0 0 #0000; -} - -[type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - border-color: #2563eb; -} - -input::-moz-placeholder, textarea::-moz-placeholder { - color: #6b7280; - opacity: 1; -} - -input::placeholder,textarea::placeholder { - color: #6b7280; - opacity: 1; -} - -::-webkit-datetime-edit-fields-wrapper { - padding: 0; -} - -::-webkit-date-and-time-value { - min-height: 1.5em; - text-align: inherit; -} - -::-webkit-datetime-edit { - display: inline-flex; -} - -::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { - padding-top: 0; - padding-bottom: 0; -} - -select { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); - background-position: right 0.5rem center; - background-repeat: no-repeat; - background-size: 1.5em 1.5em; - padding-right: 2.5rem; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; -} - -[multiple],[size]:where(select:not([size="1"])) { - background-image: initial; - background-position: initial; - background-repeat: unset; - background-size: initial; - padding-right: 0.75rem; - -webkit-print-color-adjust: unset; - print-color-adjust: unset; -} - -[type='checkbox'],[type='radio'] { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - padding: 0; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - display: inline-block; - vertical-align: middle; - background-origin: border-box; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - flex-shrink: 0; - height: 1rem; - width: 1rem; - color: #2563eb; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; - --tw-shadow: 0 0 #0000; -} - -[type='checkbox'] { - border-radius: 0px; -} - -[type='radio'] { - border-radius: 100%; -} - -[type='checkbox']:focus,[type='radio']:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); - --tw-ring-offset-width: 2px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); - box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); -} - -[type='checkbox']:checked,[type='radio']:checked { - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -[type='checkbox']:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); -} - -@media (forced-colors: active) { - [type='checkbox']:checked { - -webkit-appearance: auto; - -moz-appearance: auto; - appearance: auto; - } -} - -[type='radio']:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); -} - -@media (forced-colors: active) { - [type='radio']:checked { - -webkit-appearance: auto; - -moz-appearance: auto; - appearance: auto; - } -} - -[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { - border-color: transparent; - background-color: currentColor; -} - -[type='checkbox']:indeterminate { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -@media (forced-colors: active) { - [type='checkbox']:indeterminate { - -webkit-appearance: auto; - -moz-appearance: auto; - appearance: auto; - } -} - -[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { - border-color: transparent; - background-color: currentColor; -} - -[type='file'] { - background: unset; - border-color: inherit; - border-width: 0; - border-radius: 0; - padding: 0; - font-size: unset; - line-height: inherit; -} - -[type='file']:focus { - outline: 1px solid ButtonText; - outline: 1px auto -webkit-focus-ring-color; -} - -*, ::before, ::after { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -::backdrop { - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-rotate: 0; - --tw-skew-x: 0; - --tw-skew-y: 0; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-pan-x: ; - --tw-pan-y: ; - --tw-pinch-zoom: ; - --tw-scroll-snap-strictness: proximity; - --tw-gradient-from-position: ; - --tw-gradient-via-position: ; - --tw-gradient-to-position: ; - --tw-ordinal: ; - --tw-slashed-zero: ; - --tw-numeric-figure: ; - --tw-numeric-spacing: ; - --tw-numeric-fraction: ; - --tw-ring-inset: ; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: rgb(59 130 246 / 0.5); - --tw-ring-offset-shadow: 0 0 #0000; - --tw-ring-shadow: 0 0 #0000; - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - --tw-blur: ; - --tw-brightness: ; - --tw-contrast: ; - --tw-grayscale: ; - --tw-hue-rotate: ; - --tw-invert: ; - --tw-saturate: ; - --tw-sepia: ; - --tw-drop-shadow: ; - --tw-backdrop-blur: ; - --tw-backdrop-brightness: ; - --tw-backdrop-contrast: ; - --tw-backdrop-grayscale: ; - --tw-backdrop-hue-rotate: ; - --tw-backdrop-invert: ; - --tw-backdrop-opacity: ; - --tw-backdrop-saturate: ; - --tw-backdrop-sepia: ; - --tw-contain-size: ; - --tw-contain-layout: ; - --tw-contain-paint: ; - --tw-contain-style: ; -} - -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - -.static { - position: static; -} - -.mx-auto { - margin-left: auto; - margin-right: auto; -} - -.mb-4 { - margin-bottom: 1rem; -} - -.mt-8 { - margin-top: 2rem; -} - -.flex { - display: flex; -} - -.w-32 { - width: 8rem; -} - -.w-full { - width: 100%; -} - -.max-w-prose { - max-width: 65ch; -} - -.space-x-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-x-reverse: 0; - margin-right: calc(1rem * var(--tw-space-x-reverse)); - margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); -} - -.rounded { - border-radius: 0.25rem; -} - -.rounded-lg { - border-radius: 0.5rem; -} - -.border-2 { - border-width: 2px; -} - -.bg-gray-50 { - --tw-bg-opacity: 1; - background-color: rgb(249 250 251 / var(--tw-bg-opacity)); -} - -.bg-rose-400 { - --tw-bg-opacity: 1; - background-color: rgb(251 113 133 / var(--tw-bg-opacity)); -} - -.bg-white { - --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity)); -} - -.p-2 { - padding: 0.5rem; -} - -.p-4 { - padding: 1rem; -} - -.py-4 { - padding-top: 1rem; - padding-bottom: 1rem; -} - -.text-3xl { - font-size: 1.875rem; - line-height: 2.25rem; -} - -.text-xl { - font-size: 1.25rem; - line-height: 1.75rem; -} - -.font-bold { - font-weight: 700; -} - -.text-gray-700 { - --tw-text-opacity: 1; - color: rgb(55 65 81 / var(--tw-text-opacity)); -} - -.text-rose-400 { - --tw-text-opacity: 1; - color: rgb(251 113 133 / var(--tw-text-opacity)); -} - -.text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); -} - -.shadow { - --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); - --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); -} \ No newline at end of file