yet another static gemlog generator + converter https://hackage.haskell.org/package/gemoire
  • Haskell 93.3%
  • Nix 4%
  • HTML 1.7%
  • Just 1%
Find a file
Sena 5dc77eedf0
All checks were successful
ci/woodpecker/push/test/3 Pipeline was successful
ci/woodpecker/push/test/1 Pipeline was successful
ci/woodpecker/push/test/2 Pipeline was successful
docs(Gemoire.Converter): fix regex hyperlink
2025-02-02 13:02:54 +03:00
.woodpecker ci: update ghcs 2025-01-19 18:30:51 +03:00
example fix(Gemoire.Converter): escape stuff 2025-02-01 23:07:29 +03:00
src docs(Gemoire.Converter): fix regex hyperlink 2025-02-02 13:02:54 +03:00
test test: test for feed escaping 2025-02-02 00:38:31 +03:00
.editorconfig feat(Gemoire.Gemlog.Post): init 2024-08-18 02:53:26 +03:00
.envrc init 2024-08-03 02:32:18 +03:00
.gitattributes feat(Gemoire.Gemlog.Post): init 2024-08-18 02:53:26 +03:00
.gitignore docs(Gemoire.Template): document properly 2025-01-24 17:21:11 +03:00
cabal.project init 2024-08-03 02:32:18 +03:00
ChangeLog.md fix(Gemoire.Converter): make recursive generations work 2025-02-02 01:04:01 +03:00
flake.lock ci: update ghcs 2025-01-19 18:30:51 +03:00
flake.nix docs(Gemoire.Converter*): add documentation 2025-02-01 17:33:55 +03:00
fourmolu.yaml init 2024-08-03 02:32:18 +03:00
gemoire.cabal fix(Gemoire.Converter): make recursive generations work 2025-02-02 01:04:01 +03:00
justfile docs(example): update 2025-01-25 18:59:08 +03:00
LICENSE chore!: clean up 2024-08-07 01:41:51 +03:00
README.md docs: update readme.md 2025-02-01 17:59:11 +03:00
Setup.lhs chore!: clean up 2024-08-07 01:41:51 +03:00
stack.yaml ci: update ghcs 2025-01-19 18:30:51 +03:00
stack.yaml.lock ci: update ghcs 2025-01-19 18:30:51 +03:00
treefmt.toml chore!: clean up 2024-08-07 01:41:51 +03:00

gemoire - yet another static gemlog generator + converter

gemoire is just a basic flexible gemini blog generator, that can:

  • be configured using Haskell code,
  • use custom templates written in an authentic syntax,
  • set additional and overriding variables for formatting,
  • generate Atom and Gemini feeds.
  • and convert capsules to various formats, such as HTML and Markdown by default.

See the example directory in the repository to see how it looks like in "production".

Getting Started

Intended way for using gemoire is through Cabal scripts. To get started, you should organize your gemlog sources like so:

gemlog
├── content
│   ├── post1.gmi
│   └── post2.gmi
└── gemlog.hs

The contents directory can be as deep as you want, all posts will end up in a flat directory in the end. After setting up your files, you can start configuring your gemlog.hs, here is a simple template for you using the defaults:

#!/usr/bin/env cabal
{- cabal:
build-depends: base
             , gemoire
-}
{-# LANGUAGE OverloadedStrings #-}

import Gemoire

main :: IO ()
main = do
    let gemlog =
            Gemlog
                { title = "my gemlog"
                , author = "me"
                , sourceDir = "content"
                , baseURL = "gemini://my.website.com/path/to/gemlog"
                , postTemplate = defPost
                , gemfeedTemplates =
                      ( defGemfeed
                      , defGemfeedEntry
                      )
                , atomTemplates =
                      ( defAtom
                      , defAtomEntry
                      )
                , overrides = vempty
                }

    generatePosts gemlog "~/public_gemini/path/to/gemlog"
    generateGemfeed gemlog "~/public_gemini/path/to/gemlog/index.gmi"
    generateAtom gemlog "~/public_gemini/path/to/gemlog/atom.xml"

After setting up your configuration, you can just cd into the gemlog directory and run the generator:

$ cabal run gemlog.hs

If Cabal is causing problems, you can just install the library and use runghc instead, like so:

$ cabal install --lib gemoire
$ runghc gemlog.hs

Customizing

You can then customize your gemlog to your liking. To do that, you might want to start with changing the templates, like so:

#!/usr/bin/env cabal
{- cabal:
build-depends: base
             , text
             , gemoire
-}
{-# LANGUAGE OverloadedStrings #-}

-- gemoire uses Data.Text under the hood.
import Data.Text (unlines)
import Prelude hiding (unlines)
import Gemoire

-- The line endings will be taken care of by gemoire.
-- ...
                , postTemplate = template . unlines $
                      [ "By {$author$}."
                      , ""
                      , "{$post$}"
                      ]
-- ...

There are various variables and different useful compounds you can use in the templates. A detailed list can be found in the documentation of Gemoire.Template. Also check the default templates in the source for some inspiration!

Different special variables are available to the formatters for feeds and posts. You can see a list of those and how you can set overrides per post in the page for Gemoire.Gemlog.

Additionally, you can set overriding variables globally using the overrides variable, like so:

-- ...
                , overrides = vlist
                      [ ("variable", "new value")
                      , ("another", "overridden")
                      ]
-- ...

Converting

After generating a Gemini capsule, you might want to convert your art into different formats, e.g. to publish on the web as well. To do so, you can use something like the following:

#!/usr/bin/env cabal
{- cabal:
build-depends: base
             , gemoire
-}

import Gemoire

main :: IO ()
main = do
    let web = defWebConversion

    convertCapsule web "~/public_gemini" "~/public_html"

You can also somewhat customize this behaviour, like so:

#!/usr/bin/env cabal
{- cabal:
build-depends: base
             , gemoire
-}
{-# LANGUAGE OverloadedStrings #-}

import Gemoire

main :: IO ()
main = do
    let web = defWebConversion
                  { textTemplate = template "<p class=\"something\">{$text$}</p>"
                  , rewriteRules =
                        [ -- Rewrite .gmi links in the domain example.com as .html.
                          ( ".*\\.gmi" -- input files in which the rewriting will occur
                          , "((https*)|(gemini))://example.com/(.*)\\.gmi" -- the RegEx to match
                          , "https://example.com/\\4.html" -- the replacement text
                          , True -- multiline
                          , True -- case senstive
                          )
                        ]
                  , conversionOverrides = vlist [("title", "every title is this now")]
                  }

    convertCapsule web "~/public_gemini" "~/public_html"

You can also define your own conversion rules using the Conversion struct. See the documentation of Gemoire.Converter for more on what else you can do and the template variables.

See also