module Main where import Hakyll hiding (fromList) import Hakyll qualified import Data.Binary (Binary) import Data.String (IsString (fromString)) import Data.Time (UTCTime, defaultTimeLocale, parseTimeM) import Data.Map.Lazy qualified as Map main ∷ IO () main = hakyll $ do match "css/*.css" copyAll match "images/**" copyAll match "css/*.hs" do route toCss compile $ getResourceString >>= withItemBody (unixFilter "runghc" []) match "src/*.hs" $ compile getResourceString match "templates/*" $ compile templateBodyCompiler cards ← buildCards "notes/index.md" let dep = cardsDependency cards let ids = cardId <$> cardsList cards let all = Hakyll.fromList ids rulesExtraDependencies [dep] $ match all do route toHtml compile $ do card ← getUnderlying >>= cardsGet cards nav ← cardNav cards card pandocCompiler <&> fmap (nav <>) >>= loadAndApplyTemplate "templates/card.html" pageCtx >>= loadAndApplyTemplate "templates/default.html" pageCtx >>= relativizeUrls match "pages/*.md" $ do route $ gsubRoute "pages/" (const "") `composeRoutes` toHtml let top5 key sortBy = loadAll all >>= sortBy <&> take 5 . reverse let genCtx key = listField key pageCtx . top5 key compile . pageCompiler "templates/default.html" $ pageCtx <> genCtx "cardsModified" byModified <> genCtx "cardsPublished" byPublished pageCtx ∷ Context String pageCtx = defaultContext <> snippetField <> dateField "date" dateFormat <> modificationTimeField "dateModified" dateFormat cardNav ∷ Cards → Card → Compiler String cardNav cards self = do let selfId = cardId self let parentId = cardParent self children ← navChildren cards "" self siblings ← if parentId == cardIdNone then pure "" else cardsGet cards parentId >>= navChildren cards selfId aunties ← if parentId == cardIdNone then pure "" else do parent ← cardsGet cards parentId let grandparent = cardParent parent if grandparent == cardIdNone then pure "" else cardsGet cards grandparent >>= navChildren cards parentId pure $ "
\n" <> ("

" <> cardTitle self <> "

\n") <> aunties <> siblings <> children <> "
" where wrap s = "\n" navChildren cards cur self = wrap . htmlOf cur <$> routeChildren cards self htmlOf cur routed = mconcat $ htmlOfOne cur <$> routed htmlOfOne cur (card, path) = "
  • path <> "\"" <> (if cur == cardId card then "class=\"current\"" else "") <> ">" <> cardTitle card <> "
  • \n" routeChildren ∷ Cards → Card → Compiler [(Card, FilePath)] routeChildren cards self = do let children = cardChildren self childrenCards ← cardsGet cards `mapM` children childrenRoutes ← cardDotHtml `mapM` children pure $ zip childrenCards childrenRoutes {-parent ← cardsGet cards $ cardParent self grandparent ← cardsGet cards $ cardParent parent aunties ← cardsGet cards `mapM` cardChildren grandparent siblings ← cardsGet cards `mapM` cardChildren parent-} buildCards ∷ (MonadFail m, MonadMetadata m) ⇒ CardId → m Cards buildCards id = do cardMap ← buildCardMap cardIdNone id dependencies ← makePatternDependency "notes/*.md" pure (cardMap, dependencies) buildCardMap ∷ (MonadFail m, MonadMetadata m) ⇒ CardId → CardId → m CardMap buildCardMap parent id = do meta ← getMetadata id title ← getMetadataField' id "title" let children = cardFromSymbol <$> fromMaybe [] (lookupStringList "children" meta) let card = Card id parent children title (<>) [(id, card)] . Map.unions <$> forM children (buildCardMap id) cardFromSymbol ∷ ToString α ⇒ α → CardId cardFromSymbol = fromString . (<> ".md") . ("notes/" <>) . toString cardDotHtml ∷ CardId → Compiler FilePath cardDotHtml id = getRoute id >>= \case Just x → pure x Nothing → fail "Card must have a route" dateFormat ∷ String dateFormat = "%e %b %Y %H:%M:%S" pageCompiler ∷ Identifier → Context String → Compiler (Item String) pageCompiler template ctx = getResourceString >>= applyAsTemplate ctx >>= renderPandoc >>= loadAndApplyTemplate template ctx >>= relativizeUrls copyAll ∷ Rules () copyAll = route idRoute >> compile copyFileCompiler toHtml, toCss ∷ Routes toHtml = setExtension "html" toCss = setExtension "css" byModified, byPublished ∷ [Item a] → Compiler [Item a] byModified = sortByM modified byPublished = sortByM published modified, published ∷ Item a → Compiler UTCTime modified = getItemModificationTime . itemIdentifier published = getItemUTC defaultTimeLocale . itemIdentifier getDateField ∷ (MonadMetadata m, MonadFail m) ⇒ String → Item a → m UTCTime getDateField key x = do field ← getMetadataField (itemIdentifier x) key case field of Just str → parseTimeM True defaultTimeLocale dateFormat str Nothing → fail $ "Couldn't get field " ++ key ++ " for " ++ show (itemIdentifier x) cardIdNone ∷ CardId cardIdNone = "" type CardId = Identifier data Card = Card { cardId ∷ CardId , cardParent ∷ CardId , cardChildren ∷ [CardId] , cardTitle ∷ String } cardsMap ∷ Cards → CardMap cardsDependency ∷ Cards → Dependency cardsList ∷ Cards → [Card] cardsGet ∷ Cards → CardId → Compiler Card cardsDependency (_, d) = d cardsMap (m, _) = m cardsList = Map.elems . cardsMap cardsGet cards id = case cardsMap cards Map.!? id of Just card → pure card Nothing → fail "???" type Cards = (CardMap, Dependency) type CardMap = Map CardId Card sortByM ∷ (Monad m, Ord k) ⇒ (a → m k) → [a] → m [a] sortByM f xs = map fst . sortBy (comparing snd) <$> mapM (liftA2 fmap (,) f) xs