source

this page contains all of the Haskell code that's used to generate this website . the full source repository is on SourceHut . i've also written about the story behind this design .

Pages.hs

raw file

   1module Pages
   2  ( pageWrapper
   3  , mainPages
   4  , tagPages
   5  , postPages
   6  , projectPages
   7  , bookPage
   8  ) where
   9
  10import Zoxuli
  11import Lucid hiding (h2_, h3_, h4_, h5_, h6_)
  12import Lucid.Base (makeAttribute)
  13import Optics
  14import GHC.Generics (Generic)
  15import Data.Maybe (fromMaybe)
  16import Data.List (elemIndex, intersperse)
  17import Data.Text (Text, pack)
  18import Control.Monad (when)
  19import Control.Monad.State.Lazy (get, lift)
  20import qualified Data.Set as Set
  21
  22pageWrapper :: Bool -> Page -> Page
  23pageWrapper showNav page = page & #html .~ ( do
  24  doctype_
  25  html_ [lang_ "en"] $ do
  26    head_ $ do
  27      title_ . toHtml $ "eevie nebulæ | " <> page.title
  28      meta_ [charset_ "UTF-8"]
  29      meta_ [name_ "viewport", content_ "width=device-width, initial-scale=1"]
  30      meta_ [name_ "author", content_ "eevie nebulæ"]
  31      meta_ [name_ "description", content_ "negentropic equilibrium"]
  32      link_ [rel_ "stylesheet", href_ "/css/styles.css"]
  33      link_ [rel_ "icon", href_ "data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🌌</text></svg>"]
  34    body_ $ do 
  35      when showNav $
  36        header_ $ do
  37          nav_ $ do
  38            p_ $ link False homePage "eevie nebulæ"
  39            ul_ $ mapM_ navLink
  40              [postsPage, projectsPage, tagsPage, metaPage]
  41          hr_ []
  42      main_ $ do
  43        h1_ $ toHtml page.title
  44        page.html
  45        lift get <&> (^? #_Two % #footnotes) >>= \case
  46          Just footnotes@(_:_) -> do
  47            div_ [class_ "footnotes"] $ do
  48              h2_ "footnotes"
  49              ol_ $ mapM_ renderFootnote footnotes
  50          _ -> pure ()
  51        lift get <&> (^? #_Two % #backlinks) >>= \case
  52          Just backlinks@(_:_) -> do
  53            div_ [class_ "backlinks"] $ do
  54              h2_ "backlinks"
  55              ul_ $ mapM_ renderBacklink backlinks
  56          _ -> pure ()
  57  )
  58  where navLink :: Page -> Zoxuli
  59        navLink p = li_ $ link False p p.title
  60        renderFootnote :: Footnote -> Zoxuli
  61        renderFootnote (Footnote index content) =
  62          li_ [id_ $ "fn" <> pack (show index)] $ do
  63            content; " "
  64            a_ [ href_ $ "#r" <> pack (show index)
  65               , makeAttribute "aria-label" "return to content" ]
  66              "[back]"
  67        renderBacklink :: Link -> Zoxuli
  68        renderBacklink ln = li_ $ do
  69          toHtml ln.destinationTitle; " : \""
  70          a_ [href_ $ "/" <> ln.destinationPath <> ".html#"
  71                          <> slugify ln.body] $
  72             toHtml ln.body
  73          "\""
  74
  75-- | main pages
  76
  77mainPages :: [Page]
  78mainPages =
  79  [ homePage
  80  , metaPage
  81  , faqPage
  82  , projectsPage
  83  , postsPage
  84  , tagsPage
  85  , seriesPage
  86  , sourcePage
  87  , contactPage
  88  , todoPage
  89  , anticopyrightPage
  90  , downloadPage
  91  , digitalArtPage
  92  , photographyPage ]
  93
  94homePage :: Page
  95homePage = makePage "home" $ do
  96  h2_ "featured post"
  97  p_ $ div_ [class_ "border"] $ do
  98    p_ [style_ "font-size: 120%"] $
  99      b_ $ link True (fst theOasisPost) "the oasis"
 100    blockquote_ $ em_ "for a long time , i've been thinking about how to build a kinder & more radical version of the OASIS from Ready Player One using currently available technology [...]"
 101  p_ $ link False postsPage "more posts ..."
 102  hr_ []
 103  h2_ "featured project"
 104  p_ $ div_ [class_ "border"] $ do
 105    p_ [style_ "font-size: 120%"] $
 106      b_ $ link True (fst zoxuliProject) "zoxuli"
 107    blockquote_ $ em_ "years ago , it started with a humble goal that many could relate to . i wanted to make a website to put my writing on [...]"
 108  p_ $ link False projectsPage "more projects ..."
 109  hr_ []
 110  div_ [style_ "text-align: center"] $ do
 111    h2_ "negentropic equilibrium"
 112    p_ $ do "after all that "; b_ "pain"
 113    p_ $ do "after all that "; b_ "trauma"
 114    p_ $ do "grinding your ability to act to a "; b_ "halt"
 115    p_ $ do "you can still "; b_ "choose"; " to keep going"
 116    p_ $ do "keep "; b_ "healing"
 117    p_ $ do "keep "; b_ "fighting"
 118    p_ $ b_ "with any scrap of leverage you've got left"
 119    p_ $ em_ "for as long as agency exists"
 120
 121metaPage :: Page
 122metaPage = makePage "meta" $ do
 123  ul_ $ do
 124    li_ $ link False faqPage "frequently asked questions" 
 125    li_ $ link False contactPage "contact me" 
 126    li_ $ link False downloadPage "download this website"
 127    li_ $ link False sourcePage "source code"
 128    li_ $ link False anticopyrightPage "anticopyright"
 129    li_ $ link False todoPage "todo"
 130
 131faqPage :: Page
 132faqPage = makePage "FAQ" $ p_ $ do
 133  img_ [ src_ "/img/avatar.png"
 134       , class_ "pixel-art avatar"
 135       , alt_ "a pixel art portrait of eevie floating amongst magic sigils" ]
 136  h2_ "who are you ?"
 137  p_ $ do "hi ! i'm "; b_ "eevie"; " , pronouns she/fæ ."
 138  h2_ "what do you do ?"
 139  p_ $ do "i'm currently working on the intersection of anti-fascism , veganism , & transhumanism ."
 140  h2_ "how do i leave a comment ?"
 141  p_ $ do
 142    link True contactPage "contact me"
 143    " with your name ( optional ) & comment , & i might add it ."
 144  p_ "feel free to let me know if you notice any factual inaccuracies ior accessibility issues on my website !" 
 145  h2_ "what does ior & eor mean ?"
 146  p_ $ do
 147    "i dislike the ambiguity of the word \"or\" in cases where it could be "
 148    a_ [href_ "https://en.wikipedia.org/wiki/Logical_disjunction"] "inclusive"
 149    " eor "
 150    a_ [href_ "https://en.wikipedia.org/wiki/Exclusive_or"] "exclusive"
 151    " , so i call inclusive-or \"ior\" & exclusive-or \"eor\" ."
 152  h2_ "why do you put extra spaces around punctuation ?"
 153  p_ $ do
 154    "this style is easier for me to read . one reason is that commas & "
 155    a_ [href_ "https://en.wikipedia.org/wiki/Terminal_punctuation"] "terminal punctuation"
 156    " stand out more , so it requires less effort to keep track of where i am within the structure of a sentence ."
 157  h2_ "where am i ?"
 158  code "haskell" "go North\nlook"
 159  p_ $ do
 160    "you hover to the center of a sea of emerald grass that ripples in slow-motion waves . warm gusts guide the smell of rain through the chilled night air . your sigils pepper the orb around you , threaded by semantic strands cast off from your imagined hands . your gaze turns to the edge of the sea where the "
 161    link True (fst wallOfShardsPost) "wall of shards"
 162    " approaches forebodingly ."
 163
 164anticopyrightPage :: Page
 165anticopyrightPage = makePage "anticopyright" $ do
 166  p_ $ do
 167    "see "; link True (fst unlicensePost) "this post"
 168    " for why i am against copyright ."
 169  p_ $ do
 170    "all media , text , & other content on this website are in the public domain under "
 171    a_ [href_ "https://creativecommons.org/publicdomain/zero/1.0/?ref=chooser-v1"] "CC0 1.0 Universal"
 172    " , with the following exceptions :"
 173  ul_ $ do
 174    li_ $ do
 175      "the CSS for syntax highlighing was generated by "
 176      a_ [href_ "https://github.com/alecthomas/chroma"] "chroma"
 177      " , which is under the "
 178      a_ [href_ "https://github.com/alecthomas/chroma/blob/master/COPYING"] "MIT license"
 179    li_ $ do
 180      a_ [href_ "https://www.dafont.com/offenbacher-reform-cat.font"] "Offenbacher Reform"
 181      " by Peter Wiegel is under the "
 182      a_ [href_ "https://openfontlicense.org/"] "SIL Open Font License"
 183
 184downloadPage :: Page
 185downloadPage = makePage "download" $ do
 186  h2_ "archive"
 187  p_ "a full archive of this website is available for offline viewing :" 
 188  p_ $ a_ [href_ "/eevie.dev.tar.gz"] "download archive"
 189  h2_ "book"
 190  p_ $ do
 191    "there is a book page containing every post , designed for printing ior saving as a PDF :"
 192  p_ $ link False bookPage "view book"
 193  h2_ "source"
 194  p_ "one can clone the source repository of this site using git :"
 195  code "bash" "git clone https://git.sr.ht/~nebulae/eevie.dev"
 196
 197projectsPage :: Page
 198projectsPage = makePage "projects" $ do
 199  h2_ "art"
 200  ul_ $ do
 201    li_ $ link True digitalArtPage "digital"
 202    li_ $ a_ [href_ "https://plokta.bandcamp.com/"] "music"
 203    li_ $ link True photographyPage "photography"
 204  h2_ "software"
 205  ul_ $ do
 206    mapM_ (li_ . projectPreview) projectPages
 207    li_ $ do
 208      b_ $ a_ [href_ "https://git.sr.ht/~nebulae/concur_vite"] "Concur + Vite"
 209      " - a purescript-concur starter template"
 210    li_ $ do
 211      b_ $ a_ [href_ "yarnstar/index.html"] "[WIP] yarnstar"
 212      " - an implementation of Nova in Elm"
 213    li_ $ do
 214      b_ $ a_ [href_ "https://git.sr.ht/~nebulae/nova_conjure"] "[WIP] NovaConjure"
 215      " - interactive fiction witchcraft in Nova"
 216    li_ $ do
 217      b_ $ a_ [href_ "https://git.sr.ht/~nebulae/antibubble"] "[WIP] Antibubble"
 218      " - nested bubble world exploration game"
 219
 220postsPage :: Page
 221postsPage = makePage "posts" $ do
 222  p_ "in which i externalize the technicolor collapse of techno-synaptic maps of mind matter i’m made of ."
 223  ul_ $ mapM_ (li_ . postPreview) postPages
 224  microblog
 225
 226microblog :: Zoxuli
 227microblog = do
 228  h2_ "microblog"
 229  p_ "for shorter thoughts !"
 230  micropost "2024-12-06" $ do
 231    "low back creaking"; br_ []
 232    "dizzy when standing"; br_ []
 233    "both eyes blurring"; br_ []
 234    "can't we learn while moving ?"
 235  micropost "2024-06-09" $ do
 236    "i hate the term "
 237    a_ [href_ "https://en.wikipedia.org/wiki/Overfishing"] "\"overfishing\""
 238    " , because it implies that there’s an acceptable amount of "
 239    a_ [href_ "https://animalequality.org/issues/fish/"] "fishing"
 240    " ."
 241  where micropost :: Text -> Zoxuli -> Zoxuli
 242        micropost date content =
 243          p_ $ div_ [class_ "border"] $ do
 244            p_ $ b_ $ toHtml date
 245            p_ content
 246
 247bookPage :: Page
 248bookPage = makePage "book" $ do
 249  h2_ "chapters"
 250  ol_ $ mapM_ (li_ . toHtml . (^. #title)) pages
 251  mapM_ chapter pages
 252  where pages :: [Page]
 253        pages = (fst <$> postPages') <> (fst <$> projectPages')
 254        chapter :: Page -> Zoxuli
 255        chapter page = do
 256          hr_ []
 257          h1_ $ toHtml page.title
 258          page.html
 259
 260tagsPage :: Page
 261tagsPage = makePage "tags" $ do
 262  p_ "hypertext flowed through the cracks in the ground , illuminated by ambient amethyst light from the sky above ."
 263  ul_ $ mapM_ (li_ . tagPreview) allTags
 264
 265seriesPage :: Page
 266seriesPage = makePage "series" $ do
 267  mapM_ renderSeries allSeries 
 268
 269sourcePage :: Page
 270sourcePage = makePage "source" $ do
 271  p_ $ do
 272    "this page contains all of the "
 273    linkToAnchor True (fst howIUseComputersPost) "language" "Haskell"
 274    " code that's used to generate this website . the full source repository is on "
 275    a_ [href_ "https://git.sr.ht/~nebulae/eevie.dev"]
 276      "SourceHut"
 277    " . i've also written about the "
 278    link True (fst zoxuliProject) "story behind this design"
 279    " . "
 280  lift get <&> (^? #_Two % #srcFiles) >>= \case
 281    Just srcFiles -> mapM_ renderSrcFile srcFiles
 282    _             -> pure ()
 283  where renderSrcFile :: SrcFile -> Zoxuli
 284        renderSrcFile f = do
 285          h2_ f.name
 286          p_ $ a_ [href_ $ "/src/" <> f.name] "raw file"
 287          rawHtml f.content
 288
 289contactPage :: Page
 290contactPage = makePage "contact" $ do
 291  p_ $ do
 292    "email : " 
 293    span_ [class_ "email"] $ do
 294      "eevie@sent"; b_ "obfuscation-text"; ".com"
 295  h2_ "don't contact me if ..."
 296  p_ $ do "if you "; em_ "intentionally"; " propagate any of the following , i do "; em_ "not"; " want to hear from you :"
 297  ul_ $ do
 298    li_ "fascism"
 299    li_ "nationalism"
 300    li_ "classism"
 301    li_ "speciesism"
 302    li_ "substratism"
 303    li_ "ageism"
 304    li_ "antisemitism"
 305    li_ "racism"
 306    li_ "colorism"
 307    li_ "sexism"
 308    li_ "deathism"
 309    li_ "ableism"
 310    li_ "sanism"
 311    li_ "transphobia"
 312    li_ "enbyphobia"
 313    li_ "interphobia"
 314    li_ "aphobia"
 315    li_ "lesbophobia"
 316    li_ "homophobia"
 317    li_ "biphobia"
 318    li_ "fatphobia"
 319    li_ "pluralphobia"
 320  todo "create a PGP key"
 321
 322todo :: Text -> Zoxuli
 323todo title = link True todoPage $ "todo : " <> title
 324
 325todoPage :: Page
 326todoPage = makePage "todo" $ do
 327  ul_ $ do
 328    li_ $ do
 329      "follow Seirdy's "
 330      a_ [href_ "https://seirdy.one/posts/2020/11/23/website-best-practices/"] "best practices for inclusive textual websites"
 331    li_ $ do
 332      "make an "; inlineCode "md"; " function that uses the "
 333      a_ [href_ "https://hackage.haskell.org/package/cmark"] "cmark"
 334      " library"
 335    li_ "RSS/Atom feeds"
 336    li_ "N24 manager , swirlsaber , particle art , & helix lavender theme" 
 337  p_ "any todos across the site are linked to this page , so you can view them in the list of backlinks :"
 338
 339photo :: Text -> Text -> Zoxuli
 340photo file alt = img_
 341  [ src_ $ "/img/" <> file
 342  , style_ "width: 100%"
 343  , alt_ alt ]
 344
 345digitalArtPage :: Page
 346digitalArtPage = makePage "digital art" $ do
 347  h2_ "trans hacker fæ"
 348  photo "trans-hacker-fae-code.jpg" "a woman wearing a glass, polygonal skirt & round, glitchy sunglasses spreads her neon-colored wings. she is standing on a snowy hill beside a lamppost, & there is a city with towering skyscrapers across an ocean bay in the background. an overlay over her vision shows a terminal window with cute ASCII art borders. she just updated her source code so that she doesn't compromise with the gender police."
 349  p_ $ do
 350    "( "
 351    a_ [href_ "/img/trans-hacker-fae.jpg"] "version without overlay"
 352    " )"
 353  p_ "this image is about how i used to be so gaslit into submission that i’d frequently compromise with people who didn’t want me to exist , but now transphobes only make me want to scream & cry & then continue living on anyway in absolute , prideful defiance ."
 354  p_ $ do
 355    "made using "
 356    a_ [href_ "https://www.blender.org/"] "Blender"
 357    " , "
 358    a_ [href_ "https://godotengine.org/"] "Godot"
 359    " , & "
 360    a_ [href_ "https://krita.org/en/"] "Krita"
 361    " ."
 362  h2_ "spiky swamp"
 363  photo "spiky_swamp.jpg" "a cloaked, humanoid creature with glowing eyes & a spiky tail stands beside a gramophone on a boardwalk in a misty swamp. trees with wide bases & black spikes are scattered around."
 364  p_ "this is a screenshot from my first attempt at making a low-poly 3D game ."
 365
 366photographyPage :: Page
 367photographyPage = makePage "photography" $ do
 368  photo "banner.png" "a dithered photograph of blue & pink flowers"
 369  photo "plants5.jpg" "a beetle resting on the center of a fully bloomed flower"
 370  photo "plants4.jpg" "a small butterfly in a bright field of flowers"
 371  photo "plants1.jpg" "a brightly lit plant with bundles of fuzzy pink flowers"
 372  photo "plants2.jpg" "a bundle of fuzzy white flowers in a verdant garden"
 373  photo "plants3.jpg" "stalks of bamboo in a forest with large bokeh in the background"
 374
 375-- | tag pages
 376
 377taggedPages :: [Page]
 378taggedPages = (fst <$> projectPages) <> (fst <$> postPages)
 379
 380allTags :: [Text]
 381allTags = deduplicate $ concatMap (^. #tags) taggedPages
 382  where deduplicate = Set.toList . Set.fromList
 383
 384pagesWithTag :: Text -> [Page]
 385pagesWithTag tag = filter (elem tag . (^. #tags)) taggedPages
 386
 387tagPreview :: Text -> Zoxuli
 388tagPreview tag = do
 389  toHtml . show . length $ pagesWithTag tag; " - "
 390  a_ [href_ $ "/tags/" <> slugify tag <> ".html"] $ toHtml tag
 391
 392renderTags :: [Text] -> Zoxuli
 393renderTags tags =
 394  sequence_ . intersperse " , " $ renderTag <$> tags
 395  where renderTag :: Text -> Zoxuli
 396        renderTag tag =
 397          a_ [ href_ $ "/tags/" <> slugify tag <> ".html" ]
 398            $ toHtml tag
 399
 400tagPages :: [Page]
 401tagPages = makeTagPage <$> allTags
 402
 403makeTagPage :: Text -> Page
 404makeTagPage tag = Page tag path [] html
 405  where path = "tags/" <> slugify tag
 406        html = do
 407          p_ $ do "pages tagged \""; toHtml tag; "\":"
 408          ul_ . mapM_ (\p -> li_ $ link False p p.title)
 409            $ pagesWithTag tag
 410
 411-- | page series
 412
 413data Series = Series
 414  { name  :: Text
 415  , pages :: [Page] } deriving (Generic)
 416
 417allSeries :: [Series]
 418allSeries = [computingSeries]
 419
 420computingSeries :: Series
 421computingSeries = Series "liberatory computing"
 422  [ fst theOasisPost
 423  , fst unlicensePost
 424  , fst timelineSplittingPost
 425  , fst queerCybersecPost
 426  , fst howIUseComputersPost
 427  , fst zoxuliProject ]
 428
 429renderSeries :: Series -> Zoxuli
 430renderSeries series = do
 431  h2_ series.name
 432  ol_ . mapM_ (\p -> li_ $ link False p p.title)
 433    $ series.pages
 434
 435seriesNav :: Page -> Maybe Zoxuli
 436seriesNav page =
 437  if null seriesWithPage then Nothing
 438  else Just $ mapM_ renderSeriesNav seriesWithPage
 439  where seriesWithPage = filter (elem page . (^. #pages)) allSeries
 440        renderSeriesNav :: Series -> Zoxuli
 441        renderSeriesNav (Series name ps) = span_ $ do
 442          let next xs x = lookup x . zip xs $ tail xs
 443              maybeLink t = maybe (pure ()) $ \p -> link False p t
 444              position = (+ 1) . fromMaybe 1 $ elemIndex page ps
 445          b_ "series"; " : "
 446          toHtml . show $ position; "/"
 447          toHtml . show $ length ps
 448          " in "
 449          a_ [ href_ $ "/series.html#" <> slugify name ]
 450            . toHtml $ name
 451          " : "
 452          maybeLink "[previous]" $ next (reverse ps) page
 453          " "
 454          maybeLink "[next]" $ next ps page
 455
 456-- | project pages
 457
 458projectWrapper :: Project -> Project
 459projectWrapper project =
 460  project & _1 % #html %~ (meta <>)
 461  where meta = do
 462          p_ [class_ "description"] .
 463            toHtml $ project ^. _2 % #description
 464          toc
 465          ul_ $ do
 466            li_ $ a_ [ href_ $ project ^. _2 % #repo ] $
 467              b_ "📦 SourceHut page"
 468            maybe (pure ()) li_ . seriesNav $ fst project
 469            li_ $ do
 470              b_ "tags"; " : "
 471              renderTags $ project ^. _1 % #tags
 472
 473projectPreview :: Project -> Zoxuli
 474projectPreview project = do
 475  b_ $ link False (fst project) $ project ^. _1 % #title
 476  " - "; toHtml $ project ^. _2 % #description
 477
 478projectPages :: [Project]
 479projectPages = projectWrapper <$> projectPages'
 480
 481projectPages' :: [Project]
 482projectPages' =
 483  [ zoxuliProject
 484  , swirlmaniaProject
 485  , coaltonRaylibProject
 486  ]
 487
 488zoxuliProject :: Project
 489zoxuliProject = makeProject "zoxuli"
 490  "a lucid static site generator"
 491  "https://git.sr.ht/~nebulae/zoxuli"
 492  ["web dev", "haskell", "programming"] $ do
 493  h2_ "a humble goal"
 494  p_ "years ago , it started with a humble goal that many could relate to . i wanted to make a website to put my writing on ."
 495  p_ "this would lead me on an unexpectedly long journey that ends at the website you're on today ."
 496  h2_ "static site generators"
 497  p_ $ do
 498    "i started off by learning about "
 499    a_ [href_ "https://wordpress.com/"] "WordPress"
 500    " , then quickly realized that the WordPress interface was not accessible for me , although i would not have known to describe the problem like that at the time ."
 501  p_ $ do
 502    "looking for other options , i discovered "
 503    a_ [href_ "https://en.wikipedia.org/wiki/Static_site_generator"] "static site generators"
 504    " ( SSGs ) , which had the nice property that they were interfaced with using a text editor & a terminal window , which was more accessible to me . so i tried my best to learn an SSG called "
 505    a_ [href_ "https://jekyllrb.com/"] "Jekyll"
 506    " , but the documentation was too advanced for me at the time . i moved on to other projects for a while ."
 507  h2_ "a new problem"
 508  p_ "a while later , after i had learned more about programming for other reasons , i realized i could now understand the Jekyll documentation !"
 509  p_ $ do "except ... now my expectations for my website had changed significantly . now i wanted :"
 510  ul_ $ do
 511    li_ "excellent accessibility"
 512    li_ "post series with navigation"
 513    li_ "tables of contents"
 514    li_ "tags that work across multiple categories of pages"
 515    li_ "backlinks"
 516    li_ "footnotes"
 517    li_ "breadcrumbs"
 518    li_ "control over where & how the aforementioned elements appear"
 519    li_ "syntax highlighting"
 520    li_ "LaTeX"
 521    li_ "semantic markup"
 522    li_ "no JavaScript"
 523    li_ "broken link checking"
 524    li_ "simple & powerful extensibility in a comfortable language"
 525    li_ "the ability to write pages in any markup format"
 526    li_ "a fast development feedback loop"
 527  p_ $ do
 528    "i thought , hey , that's a lot of stuff to ask for , but there are "
 529    a_ [href_ "https://jamstack.org/generators/"] "hundreds"
 530    " ( eor likely thousands ) of SSGs , so surely at least one of them can do all that ?"
 531  h2_ "SSG frenzy"
 532  p_ $ do
 533    "i tried a lot of SSGs . the three that got me closest to all of my goals were "
 534    a_ [href_ "https://soupault.app/"] "Soupault"; " , "
 535    a_ [href_ "https://bagatto.co/"] "Bagatto"; " , & "
 536    a_ [href_ "https://jaspervdj.be/hakyll/"] "Hakyll"
 537    " . but none of those got me everything ( "
 538    todo "explain their strengths & shortcomings in detail"
 539    " ) . there might be another one out there , but i didn't find it ."
 540  p_ "not to worry though , because after trying so many SSGs, i was intimately familiar with the tradeoffs involved in their design , & felt comfortable writing my own from scratch ."
 541  h2_ "zoxuli : first attempt" 
 542  p_ $ do
 543    "my first attempt ( "
 544    todo "share zoxuli v1"
 545    " ) was written in Common Lisp , & was most similar to Bagatto ."
 546  p_ $ do
 547    "the core idea was to have the user of the zoxuli library write a data structure describing the inputs & outputs of the site in Lisp , & the markup files ( ior stylesheet , ior whatever else - it was filetype agnostic ) could contain snippets of lisp surrounded by "
 548    inlineCode ".("; " & "; inlineCode ")."
 549    " that would be evaluated when the site was built ."
 550  p_ $ do
 551    "this meant that - as an example - if one wanted to generate backlinks , one could create a "
 552    inlineCode ".(link <args>)."
 553    " function that would be used anywhere they needed links in their markup . the function would both render the link HTML ( using "
 554    a_ [href_ "https://github.com/ruricolist/spinneret"] "spinneret"
 555    " ) that gets inserted back into that position by zoxuli , & also store the link data into a variable . during the second pass , calls to a "
 556    inlineCode ".(backlinks)."
 557    " function would convert the collected links into backlinks & render them into HTML wherever they are needed ."
 558  h2_ "zoxuli : final version"
 559  p_ "after giving version one a proper go , these were my issues with it :"
 560  ul_ $ do
 561    li_ "i ended up using so many Lisp functions in the markup that i felt like i would've been better off just writing the markup in spinneret" 
 562    li_ "my implementation resulted in slower build times than i had hoped"
 563    li_ $ do
 564      "around that time , i realized i don't like programming in Lisp nearly as much as Haskell , especially after learning "
 565      a_ [href_ "https://www.parsonsmatt.org/2018/05/19/ghcid_for_the_win.html"] "how to use ghcid"
 566      " properly"
 567    li_ $ do
 568      "i also discovered the "
 569      a_ [href_ "https://hackage.haskell.org/package/lucid"] "lucid"
 570      " library for Haskell that i favored over spinneret for a few reasons"
 571  p_ "incorporating these observations , the final version of zoxuli is basically just a thin scaffold around lucid . the only thing from my prior criteria for an SSG that it doesn't support is the ability to use any markup format , but i enjoy writing lucid more than any other markup format i've tried , so that doesn't bother me ."
 572  p_ $ do
 573    "i ported this website from Hakyll to zoxuli & i'm quite pleased with the experience . the source code for this website is on the "
 574    link True sourcePage "source page"
 575    " . "
 576
 577swirlmaniaProject :: Project
 578swirlmaniaProject = makeProject "swirlmania"
 579  "a StepMania level processor"
 580  "https://git.sr.ht/~nebulae/swirlmania"
 581  ["stepmania", "exercise", "rust"] $ do
 582  h2_ "what it is"
 583  p_ $ do
 584    "swirlmania is a CLI "
 585    a_ [href_ "https://www.stepmania.com/"] "StepMania"
 586    " simfile ( .ssc ) processor with support for generating tech charts without double steps ."
 587  p_ "swirlmania has five modes for different levels of \"tech\" difficulty . put simply , \"tech\" here refers to how far the player's body may need to turn to hit the arrow patterns ."
 588  h2_ "why i made it"
 589  p_ $ do
 590    "i enjoy playing "
 591    a_ [href_ "https://projectoutfox.com/"] "Project OutFox"
 592    " ( a fork of StepMania ) on a dance pad for exercise , but finding simfiles ( which may also be refered to as charts/songs/levels ) that are of a consistent difficulty level can be challenging . i also have a strong preference for mid-speed technical maps without jumps , which makes things even more challenging . matching those criteria severely limits the song selection , but i would prefer to choose from any available level pack ."
 593  p_ $ do
 594    "charting my own simfiles , eor processing levels by hand is far too costly to my time for what is intended to be an exercise device , so i decided to write a program to process pre-existing levels automatically . there are a few level processors already in existence such as "
 595    a_ [href_ "https://github.com/phr00t/AutoStepper"] "AutoStepper"
 596    " , but as far as i can tell , none of them consistently maintain flow ( no double steps ) at higher tech levels . i'd also like to support more tech patterns in the future ."
 597
 598coaltonRaylibProject :: Project
 599coaltonRaylibProject = makeProject "coalton-raylib"
 600  "games programming in Coalton"
 601  "https://git.sr.ht/~nebulae/coalton-raylib"
 602  ["game dev", "coalton", "common lisp", "programming"] $ do
 603  h2_ "the problem"
 604  p_ "i love programming in ML-family languages like Haskell & OCaml , but they typically aren't interactive like Common Lisp , which makes programming games more difficult ."
 605  p_ $ do
 606    "this is because one must restart the entire program they're working on every time a new change is compiled"
 607    footnote "there are ways to smooth this over , but they can add a lot of complexity"
 608    " ."
 609  h2_ "one possible solution"
 610  p_ $ do
 611    "as an ML language built on top of Common Lisp , "
 612    a_ [href_ "https://coalton-lang.github.io/20211010-introducing-coalton/"] "Coalton"
 613    " can provide a statically typed , functional ( with typeclasses ! ) , & highly interactive game programming environment ."
 614  p_ $ do
 615    "coalton-raylib is an example project i've shared that uses the "
 616    a_ [href_ "https://www.raylib.com/"] "raylib"
 617    " graphics library ."
 618  p_ $ do
 619    "i've written the draw loop in Common Lisp , & the data model & update functions in Coalton"
 620    footnote "that's just the method i found to be the most ergonomic , but i'm certain that others could find better ways of architecting a game with Coalton"
 621    " . this means that data & functions can be selectively reevaluated while the game is running , all while benefiting from compile-time type errors if a mistake is made !"
 622  h2_ "conclusion"
 623  p_ "this was just a small experiment , that i figured i'd share in case it's useful for anyone else . i'm not sure if i'll use this for a larger game eor not , given that Coalton hasn't reached 1.0 yet , but it's great to know that it's a possibility ."
 624
 625-- | post pages
 626
 627postWrapper :: Post -> Post
 628postWrapper post =
 629  post & _1 % #html %~ (meta <>)
 630  where meta = do
 631          toc
 632          ul_ $ do
 633            li_ $ b_ $ toHtml $ post ^. _2 % #date
 634            maybe (pure ()) li_ . seriesNav $ fst post
 635            li_ $ do
 636              b_ "tags"; " : "
 637              renderTags $ post ^. _1 % #tags
 638
 639postPreview :: Post -> Zoxuli
 640postPreview post = do
 641  link False (fst post) $ post ^. _1 % #title
 642
 643postPages :: [Post]
 644postPages = reverseChronological $ postWrapper <$> postPages'
 645
 646postPages' :: [Post]
 647postPages' =
 648  [ theOasisPost
 649  , timelineSplittingPost
 650  , compulsorySchoolingPost
 651  , howIUseComputersPost
 652  , unlicensePost
 653  , veganTipsPost
 654  , queerCybersecPost
 655  , inferiPost
 656  , wallOfShardsPost ]
 657
 658theOasisPost :: Post
 659theOasisPost = makePost "the oasis" "2024-11-25"
 660  ["virtual reality", "security", "game dev", "programming", "inclusive design", "formal verification"] $ do
 661  h2_ "introduction"
 662  p_ $ do
 663    "for a long time , i've been thinking about how to build a kinder & more radical version of the "
 664    a_ [href_ "https://readyplayerone.fandom.com/wiki/OASIS"] "OASIS"
 665    " from "
 666    a_ [href_ "https://en.wikipedia.org/wiki/Ready_Player_One"] "Ready Player One"
 667    " using currently available technology . this page outlines my current vision for it . i'll be throwing a lot of ideas together somewhat haphazardly , so expect unintended inconsistencies : this is more of an aspirational wishlist than a formal specification ."
 668  h2_ "motivation"
 669  p_ "gaps in our scientific knowledge limit the range of actions we can take , & some of those gaps could take eons to fill . i view simulated worlds as a stopgap that can approximate what it would be like to take actions outside of that range . categorical examples :"
 670  ul_ $ do
 671    li_ $ do
 672      "unlimited "
 673      a_ [href_ "#agency-over-avatar-&-environment"] "creative expression"
 674    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Morphological_freedom"] "morphological freedom"
 675    li_ "realistic interaction between people any distance apart"
 676    li_ $ do
 677      a_ [href_ "#formal-verification"] "formally verified"
 678      " "; a_ [href_ "#abuse-protection"] "abuse protection"
 679  h2_ "primary values"
 680  h3_ "agency over avatar & environment"
 681  blockquote_ [cite_ "https://applied-langua.ge/posts/here-is-a-loud-announcement.html#orga51f75c"] $ do
 682    p_ "Maximalist computing is designing protocols and platforms which support doing \"everything\", and in a pleasant manner. It is, in one way, an extension of the idea of computing, which is to simulate anything"
 683    p_ $ do
 684      " — Applied Language , "
 685      cite_ $ a_ [href_ "https://applied-langua.ge/posts/here-is-a-loud-announcement.html#orga51f75c"] "Maximalist computing"
 686  ul_ $ do
 687    li_ $ do
 688      "programmable , like "
 689      a_ [href_ "https://resonite.com/"] "Resonite"
 690      " is using "
 691      a_ [href_ "https://wiki.resonite.com/ProtoFlux"] "ProtoFlux"
 692      " ; the freedom to modify systems . see "
 693      a_ [href_ "https://youtu.be/eS8KeCOJBFA?si=WiUEa41EypVRfGvP"] "Rustybot's video on programming a flying device"
 694      " for a demonstration"
 695    li_ $ do
 696      "introspection to the core ; the ability to inspect any property of the oasis from inside it . this facilitates "
 697      a_ [href_ "https://plato.stanford.edu/entries/agency/"] "agency"
 698      " , if the fulfillment of one's agency's "
 699      a_ [href_ "https://plato.stanford.edu/entries/action/#ConsAim"] "constitutive aim"
 700      " is contingent on having accurate models of the world"
 701    li_ $ do "no "; a_ [href_ "https://www.deceptive.design/types"] "deceptive design patterns"; " ior advertisements"
 702    li_ $ do "no "; a_ [href_ "https://en.wikipedia.org/wiki/Artificial_scarcity"] "artificial scarcity"
 703    ul_ $ do
 704      li_ $ do link True (fst unlicensePost) "public domain"; " source code"
 705      li_ "anticapitalist ( unlike the OASIS )"
 706      li_ "no leveling hierarchy that locks features for new people"
 707      li_ $ do "no "; a_ [href_ "https://www.defectivebydesign.org/what_is_drm"] "DRM"
 708      li_ $ do "no "; a_ [href_ "https://youtu.be/YQ_xWvX1n9g?si=G0hjss5DYLXjdKPe"] "NFTs"; " ior cryptocurrencies"
 709      li_ "no limits on changing account metadata like usernames , with the exception of a UUID for persistent blocklists"
 710  h3_ "inclusive design"
 711  blockquote_ [cite_ "https://100daysofa11y.com/2019/12/03/accommodation-versus-inclusive-design/"] $ do
 712    p_ $ do "physical spaces have to find a \"universal\" solution ("; b_ "one size fits all"; ") once those spaces are built. In digital spaces, we have the flexibility of taking the \"inclusive\" approach to tailor our product so that "; b_ "one size fits one"
 713    p_ $ do
 714      " — Amy Carney , "
 715      cite_ $ a_ [href_ "https://100daysofa11y.com/2019/12/03/accommodation-versus-inclusive-design/"] "Accommodation versus Inclusive Design"
 716  ul_ $ do
 717    li_ $ do
 718      "create a core protocol like "
 719      a_ [href_ "https://en.wikipedia.org/wiki/Secure_Scuttlebutt"] "SSB"
 720      " eor "
 721      a_ [href_ "https://en.wikipedia.org/wiki/ActivityPub"] "ActivityPub"
 722      " to allow for multiple apps built upon it . "
 723      a_ [href_ "https://en.wikipedia.org/wiki/Inclusive_design"] "inclusive design"
 724      " suggests that a greater number of interfaces , each specialized for different groups of users , can result in more inclusive interfaces than a single interface for everyone would be" 
 725    li_ $ do
 726      a_ [href_ "https://iep.utm.edu/qualia/"] "qualia"
 727      "-agnostic object representation ; don't privilege one type of experience over another . for example , the data representing a teacup in the oasis should not favor a 3D mesh for visual representation any more ior less than a textual description of it . it follows that all aspects of the oasis could be experienced as parser-based "
 728      a_ [href_ "https://www.ifwiki.org/Parser-based_interactive_fiction"] "interactive fiction"
 729      " , ior more specifically , as a "
 730      a_ [href_ "https://en.wikipedia.org/wiki/Multi-user_dungeon"] "MUD"
 731      " . pure text can be accessed over a sweeping range of input & output devices , such as "
 732      a_ [href_ "https://en.wikipedia.org/wiki/Refreshable_braille_display"] "refreshable braille displays"
 733      " . here are few examples of what creation & programming could look like within interactive fiction :"
 734    ul_ $ do
 735      li_ $ a_ [href_ "https://netjam.org/quoth/"] "Quoth"
 736      li_ $ a_ [href_ "https://github.com/seisatsu/DennisMUD"] "DennisMUD"
 737      li_ $ a_ [href_ "https://wiki.xxiivv.com/site/paradise.html"] "Paradise"
 738      li_ $ a_ [href_ "https://git.sr.ht/~nebulae/nova_conjure"] "NovaConjure"
 739    li_ $ do
 740      "support many programming paradigms to accommodate varied neurotypes & prevent a "
 741      a_ [href_ "https://permacomputing.net/monoculture/"] "monoculture"
 742      " . the base language should make the creation of new languages ( spanning the range of each "
 743      a_ [href_ "https://madhadron.com/programming/seven_ur_languages.html"] "ur-language"
 744      " , & beyond ) simpler , as "
 745      a_ [href_ "https://beautifulracket.com/appendix/domain-specific-languages.html"] "Racket"
 746      " ior "
 747      a_ [href_ "https://wiki.haskell.org/Embedded_domain_specific_language"] "Haskell"
 748      " do"
 749    ul_ $ do
 750      li_ $ do
 751        "this could allow esoteric languages with radically different approaches like "
 752        a_ [href_ "https://www.dangermouse.net/esoteric/piet/samples.html"] "Piet"
 753        " to flourish"
 754      li_ $ do
 755        link True (fst coaltonRaylibProject) "Coalton"
 756        " & "
 757        a_ [href_ "https://hazel.org/"] "Hazel"
 758        " show that "
 759        a_ [href_ "https://en.wikipedia.org/wiki/ML_(programming_language)"] "ML"
 760        "-style programming can integrate into a highly dynamic simulation"
 761    li_ "thorough documentation for people with any degree of computing knowledge"
 762    li_ $ do
 763      "provide an accessibility toolkit so people can create new assistive devices within the oasis more easily , & handle "
 764      a_ [href_ "https://en.wikipedia.org/wiki/Internationalization_and_localization"] "i18n & l10n"
 765    li_ "avoid anthropocentric assumptions to future-proof the possible inclusion of non-human animals , AI agents , aliens , etc."
 766    li_ $ do
 767      "allow "
 768      a_ [href_ "https://pluralpedia.org/w/Plurality"] "plural"
 769      " systems to easily switch between profiles to match who is fronting , along with tools for communication between headmates"
 770    li_ $ do
 771      "runs natively on Linux , "
 772      a_ [href_ "https://en.wikipedia.org/wiki/Berkeley_Software_Distribution"] "BSD"
 773      " , & others"
 774    li_ $ do
 775      "see the "
 776      a_ [href_ "https://gameaccessibilityguidelines.com/full-list/"] "Game Accessibility Guidelines"
 777      " for more ideas in this realm"
 778  h3_ "abuse protection"
 779  p_ "most multiplayer video games are woefully unequipped to protect the average player from abuse , let alone marginalized players . while the prevalence of abusive people is primarily a social problem , there are still plenty of ways the oasis could ( technologically , since it's a digital space ) protect people from them , in the same way that encryption can protect online accounts from being hacked ."
 780  p_ "abuse protection is perhaps the most challenging aspect to encode into a software system , because adversaries can quickly render one's defenses obsolete by finding vulnerabilities . given this , the oasis will require enough flexibility for the people most affected by abuse to have the agency to change the design of those defenses ."
 781  ul_ $ do
 782    li_ $ do
 783      "use fine-grained "
 784      a_ [href_ "https://crochet.qteati.me/docs/reference/system/security/why.html"] "capability security"
 785      " for "
 786      a_ [href_ "https://medium.com/@robotlolita/dependencies-are-bad-but-necessary-806f38f65df4"] "safer dependencies"
 787      " while programming in the oasis ( like "
 788      a_ [href_ "https://crochet.qteati.me/docs/reference/system/whats-crochet.html"] "Crochet"
 789      " does ) , & for limiting the capabilities other people have for interacting with you . a simple example of the latter : if somebody wanted to place a hat they made onto your avatar , you would need to grant them the \"attach objects to avatar\" capability before they could do so"
 790    li_ $ do "sometimes it can be hard to defend yourself while you're being attacked . the ability to give "; em_ "highly"; " trusted people control over a subset of your capabilities could offset some of the burden & allow for faster responses to abuse . this control could be overridden ior revoked at any time"
 791    li_ $ do
 792      "alternatives to the standard block/report/votekick options should be explored . in my experience , blocking is implemented too weakly , reporting is too slow & relies on a moderation hierarchy , & votekicks require social consensus , which can be nearly impossible to acquire if one's a target of bigotry . see my post on the "
 793      link True (fst timelineSplittingPost) "timeline splitting block"
 794      " for more on this topic"
 795    li_ $ do
 796      "distributed/decentralized to counter emergent hierarchy & co-optation by corporations ior the state . the protocol should be ( non-blockchain ) "
 797      a_ [href_ "https://en.wikipedia.org/wiki/Peer-to-peer"] "P2P"
 798      " by default , with optional "
 799      a_ [href_ "https://en.wikipedia.org/wiki/Federation_(information_technology)"] "federation"
 800      " for specific instances where it's useful"
 801    li_ $ do
 802      "forums , git hosting , & any other external infrastructure should avoid regime-compliant software like Discord & GitHub if possible , & have a moderation policy that is "
 803      a_ [href_ "https://raddle.me/wiki/defining_our_terms"] "intolerant of bigotry"
 804    li_ $ do
 805      "multi-factor authentication , including "
 806      a_ [href_ "https://en.wikipedia.org/wiki/Universal_2nd_Factor"] "U2F"
 807    li_ $ do
 808      "accessible over "
 809      a_ [href_ "https://www.torproject.org/"] "TOR"
 810      " & "
 811      a_ [href_ "https://geti2p.net/en/"] "I2P"
 812    li_ $ do
 813      a_ [href_ "https://en.wikipedia.org/wiki/Homomorphic_encryption#Fully_homomorphic_encryption"] "fully homomorphic encryption"
 814      " could allow people to rely on the computational resources of others without necessarily trusting them"
 815    li_ $ do
 816      "compatible with "
 817      a_ [href_ "https://permacomputing.net/salvage_computing/"] "salvage computing"
 818      " . if the oasis is to be built upon new hardware , it must wait until the current system of "
 819      a_ [href_ "https://youtu.be/vmtaWQxBYFk?si=UotkxTCkwUbPqgQ4"] "exploitative electronics production"
 820      " is destroyed , & a kinder means of production has taken its place"
 821  h3_ "formal verification"
 822  p_ "preventing bugs becomes increasingly important as immersion improves . suppose someone uses a device that lets them feel physical pain within the oasis . when other people have the capability to physically torture them , bugs could cause the usual protection mechanisms to fail , resulting in extremely traumatic experiences ."
 823  ul_ $ do
 824    li_ $ do
 825      "deductive verification using "
 826      a_ [href_ "https://en.wikipedia.org/wiki/Dependent_type"] "dependently typed"
 827      " programming ( a "
 828      a_ [href_ "https://en.wikipedia.org/wiki/Formal_verification"] "formal verification"
 829      " approach ) could ensure , using mathematical proofs , that entire sets of bugs cannot occur . this would be used in conjunction with "
 830      a_ [href_ "https://en.wikipedia.org/wiki/Secure_by_design"] "secure design"
 831      " to prevent exploits in addition to bugs"
 832    li_ "dependent types are notoriously time consuming to learn about ( i am still a beginner ) , & would likely significantly decrease the number of people who could contribute to that portion of the codebase . here are a few possible ways to alleviate this issue :"
 833    ul_ $ do
 834      li_ "improve the learning materials . it is uncertain how much this would help , but i am hopeful over a very long time frame"
 835      li_ $ do
 836        "use "
 837        a_ [href_ "https://en.wikipedia.org/wiki/Refinement_type"] "refinement types"
 838        " where possible . "
 839        a_ [href_ "https://ucsd-progsys.github.io/liquidhaskell/"] "LiquidHaskell"
 840        " could be combined with "
 841        a_ [href_ "https://wiki.portal.chalmers.se/agda/pmwiki.php"] "Agda"
 842        " using "
 843        a_ [href_ "https://agda.github.io/agda2hs/"] "agda2hs"
 844        " , for instance"
 845      li_ $ do
 846        a_ [href_ "https://github.com/magmide/magmide"] "Magmide"
 847        " is a research project trying to solve this issue , & introduces something called the \"split Logic/Host\" architecture that could be promising , but i don't fully understand it yet"
 848    li_ $ do
 849      "the core protocol implementation should be as small as possible , to make it easier to understand & audit . this goal could extend to the implementation language itself , by choosing a simple theorem prover like "
 850      a_ [href_ "https://medium.com/@maiavictor/towards-a-simple-theorem-prover-5005a1e66a6f"] "Cedille"
 851      " , eor "
 852      a_ [href_ "https://whatisrt.github.io/meta-cedille/2019/06/25/syntactic-metaprogramming-i.html"] "Meta-cedille"
 853      " if syntactic metaprogramming were helpful"
 854  h2_ "further reading"
 855  p_ "i still have much to learn about P2P protocols , capability security , & dependent types before i could begin an implementation . here are a few documents i'd like to read next ( all freely available online ) :"
 856  ul_ $ do
 857    li_ $ a_ [href_ "https://en.wikibooks.org/wiki/The_World_of_Peer-to-Peer_(P2P)"] "The World of Peer-to-Peer"
 858    li_ $ a_ [href_ "https://ssbc.github.io/scuttlebutt-protocol-guide/"] "the Scuttlebutt Protocol Guide"
 859    li_ $ a_ [href_ "https://softwarefoundations.cis.upenn.edu/"] "Software Foundations"
 860    li_ $ a_ [href_ "https://plfa.github.io/"] "Programming Language Foundations in Agda"
 861    li_ $ a_ [href_ "https://beautifulracket.com/"] "Beautiful Racket"
 862    li_ $ a_ [href_ "https://crochet.qteati.me/docs/reference/system/index.html"] "The Crochet System"
 863    li_ $ a_ [href_ "https://fuchsia.dev/fuchsia-src/concepts/principles/secure"] "Fuchsia OS security docs"
 864  h2_ "conclusion"
 865  p_ "many subsets of this outline already exist to various degrees across cyberspace , so there are plenty of already solved problems to build on top of ."
 866  p_ $ do
 867    "that said , this is an enormous project , & not something i could do alone . if you know of ways to refine ior contribute to the oasis , feel free to "
 868    link True contactPage "contact me"
 869    " !"
 870
 871timelineSplittingPost :: Post
 872timelineSplittingPost = makePost "timeline splitting block" "2024-11-16"
 873  ["game dev", "security", "queer"] $ do
 874  h2_ "safety features aren't strong enough"
 875  p_ $ do
 876    "when i've visited social spaces in "
 877    a_ [href_ "https://en.wikipedia.org/wiki/Virtual_reality"] "virtual reality"
 878    " , i am often harassed to an extreme degree because my \"tranny faggot\" voice outs me as a subhuman worthy of death , according to them . some of the insufficiencies i've noticed in current implementations of blocking , reporting , & votekicking are as follows :"
 879  ul_ $ do
 880    li_ "there are typically too many bigots in the room for a votekick to get the majority , rendering it fruitless against the pervasiveness of transmisogyny"
 881    li_ "sometimes people will follow me around & cover my face with their hands eor another object so i can't see the menu to block them . eor they will scream in my ears so i can't focus"
 882    li_ "in some games i can't reliably obtain a person's username in order to block them , because there's an easy way for them to hide it from me temporarily , like running away"
 883    li_ "if they notice that i'm planning to block them , because i'm navigating to that portion of the menu , ior i've just blocked one of their collaborators , they may votekick me in response . in some games , being kicked resets everything that was open on the menu , so there's no way to find that person again to complete the block"
 884    li_ "even if i were able to , blocking 5+ people every time i join a new lobby would get rather tedious using the current menu interfaces"
 885    li_ "when i block someone , they can still affect the environment around them . this could entail writing hateful messages on objects , throwing things at me , destroying things i'm trying to create , distracting people i actually want to talk to , etc."
 886    li_ "when other people can't see who i've blocked , i inevitably waste time explaining how i can't hear the vile drivel that blocked people are spraying at me . this gets confusing when there are a lot of blocked & unblocked people in the same location"
 887    li_ "reports i've made haven't resulted in much action , & if they do , it usually takes at least a week"
 888  h3_ "trivial fixes"
 889  p_ "potential patches for some of the above issues :"
 890  ul_ $ do
 891    li_ "don't make social consensus necessary to completely remove someone from your environment"
 892    li_ "allow hiding & muting your surroundings while using the menu"
 893    li_ "ensure it's easy to see the username of anyone you're interacting with"
 894    li_ "add an option to share who you've blocked with others , along with your reasons for each block"
 895    li_ "allow blocking discreetly & in bulk"
 896    li_ "add a shortcut so you can block faster"
 897  h2_ "a more potent kind of block"
 898  p_ $ do "my experiences have motivated me to think of an entirely different way to implement blocking ."
 899  p_ $ do
 900    "suppose you're in "
 901    link True (fst theOasisPost) "the oasis"
 902    " , & you decide to block someone named S because they're telling you to kill yourself . in this thought experiment , blocking simply hides their avatar & audio from you . after being blocked , they draw the message \"kill yourself\" on an empty whiteboard ."
 903    p_ "should the message be visible to you ?"
 904  p_ "if you can see it , then S has successfully worked around the blocking mechanism , even if it's to a lesser degree than before . they've still violated your boundary ."
 905  p_ $ do "if you can't see it , then how would the oasis hide the message from you ? other people may still want to see S's influence on the world . this would require the world to be split into two separate states at once , which could be thought of as different "; em_ "timelines"; " ."
 906  p_ "so , if you perform a timeline splitting block ( TSB ) on S , you will enter new timeline forked off from the current one . everybody else will remain in the original timeline , but they will be given the option to join you ."
 907  h3_ "timeline divergence"
 908  p_ "a cross-timeline \"overlay\" could show people's avatars & audio from the original timeline on top of your fork , but this would only be useful temporarily . divergence between timelines over time would degrade cross-timeline communication about the world ."
 909  p_ "as time goes on , the pressure for people to pick a side in conflicts would increase . a TSB is intended as a last resort , because it removes most of the blocked person's ability to causally affect you , & possibly others ."
 910  p_ $ do
 911    "sophisticated tools for merging timelines à la "
 912    a_ [href_ "https://git-scm.com/docs/git-merge"] "git"
 913    " could prevent people from losing changes when switching timelines ."
 914  h3_ "choosing timelines"
 915  p_ "another question : how will people who were less involved in the conflict know which timeline to join ? they would need to ask around , if it mattered to them . people would have the ability to visit any timeline they're not blocked in , to assist inquiry . additionally , the menu could have a place to write your reason for a TSB , that would be shown in notifications , ior possibly a history log ."
 916
 917compulsorySchoolingPost :: Post
 918compulsorySchoolingPost = makePost "compulsory schooling is abusive" "2024-10-11"
 919  ["youth liberation", "philosophy"] $ do
 920  h2_ "even if ..."
 921  p_ "even if you didn't feel confined while in school , it doesn't mean that none of the other kids could tell they were in confinement ."
 922  p_ "even if schools are sometimes better than the life a kid would have to live at home , it doesn't mean that they're not in confinement ."
 923  p_ "even if schools sometimes manage to teach kids things they actually care about , it doesn't mean that they're not , in many ways , treated like prison inmates in the process ."
 924  h2_ "the problem"
 925  p_ $ do "coercing an entire age group of people into spending the majority of their waking hours in an environment they didn't choose , around people they didn't choose , eating food they didn't choose , doing tasks they didn't choose , holding their bodies in positions they didn't choose , all while possibly being assaulted physically ior emotionally by bullies with no true recourse , "; em_ "is abusive ."
 926  p_ $ do
 927    "if someone did the same thing to "; b_ "all"; " adults"
 928    footnote "not entirely hypothetical ; wage slavery approaches a weaker form of this"
 929    " , & justified it by saying that it's some variation of being \"for their own good !\" , would that be okay ? i sure don't think so , & adult supremacy prevents many people who agree from extrapolating that judgement to children ."
 930  p_ "even as an adult , i'm still resentful of every adult who was knowingly complicit in keeping all of my friends & i in confinement when i was young . i refuse to simply \"forgive & forget\" the traumatizing dehumanization of adult supremacy , as one particularly childist relative still demands of me ."
 931  h2_ "how to fix it"
 932  p_ "my take is fairly straightforward :"
 933  ul_ $ do
 934    li_ "make educational facilities & resources entirely opt-in"
 935    li_ "make each component of those resources granularly opt-in . give kids the ability to only go the amount that they feel like , & no more than that !"
 936    li_ "actually give children agency over the design of those resources , & give them full control over what they get to learn about"
 937    li_ "stop conditioning adult staff to act like fucking cops"
 938    li_ "be extremely cautious about creating pressure on children to visit any given educational environment"
 939  p_ $ do
 940    "unfortunately , this is only part of the equation . for this to be a fully satisfying solution , many other things about our cultures would need to radically change as well . one such example would be the creation of far more safe , uncommercialized \""
 941    a_ [href_ "https://en.wikipedia.org/wiki/Third_place"] "third places"
 942    "\" than are currently available ."
 943  hr_ []
 944  h2_ "an aside on argumentation for youth liberation"
 945  p_ "it saddens me how infrequently adults believe ior show empathy for those of us who talk openly about the ways adult supremacy has traumatized us ."
 946  p_ "it's also sad when even the people who ostensibly agree that ( for example ) compulsory education is a problem decide to only argue against it through an adult supremacist frame , measuring any potential harm by how much it deprives children of attributes that only the adults around them deemed desirable ."
 947  p_ "for instance , they'll argue that an oppressive school environment makes children uncomfortable , & that is bad because then they can't \"learn\" ( the subjects chosen by adults ) as effectively , & that is only bad because then they can't \"operate in society\" ( which if one inquires further , is usually some stand-in for greasing the cogs of capitalism & the state ) as smoothly , ior know how to \"respect authority\" ( i.e. learn to interact frictionlessly with everyone else who takes for granted & is complicit in the abuse ) properly ."
 948  p_ $ do
 949    "but what they blatantly omit from their arguments is that compulsory schooling is a problem "
 950    em_ "because it's abusive to assert ownership over another person , steal their agency , & gaslight them about their personhood"
 951    " ."
 952  p_ "if one fully believes that children are people , they should be able to confidently state why compulsory schooling should be abolished using reasons that aren't downstream of potential benefits to adults . doing otherwise only serves to deepen the power dynamic along another axis ."
 953
 954queerCybersecPost :: Post
 955queerCybersecPost = makePost "queer cybersecurity resources" "2024-07-07"
 956  ["security", "privacy", "queer", "guide"] $ do
 957  h2_ "introduction"
 958  p_ "this is a list of resources for improving one's digital security posture , prioritizing relevance to the risks queer folks face . i hope it's helpful !"
 959  p_ $ do b_ "note"; " : these resources may have changed since i've shared them , & i don't necessarily endorse every strategy they recommend . security is highly contextual . try to confirm that any advice you encounter is not factually inaccurate , outdated , ior coming from a hostile source before incorporating it into your strategy ."
 960  h2_ "privacy & security"
 961  "these links are rather general in purpose , so you may need to explore them a bit to find what you are looking for :"
 962  ul_ $ do
 963    li_ $ a_ [href_ "https://gendersec.tacticaltech.org/wiki/index.php/Complete_manual"] "Zen & the art of making tech work for you - Gender & Tech Resources"
 964    li_ $ a_ [href_ "https://ssd.eff.org/"] "Surveillance Self-Defense - Electronic Frontier Foundation"
 965    li_ $ a_ [href_ "https://www.privacyguides.org/en/"] "Privacy Guides"
 966  h2_ "doxing"
 967  p_ "these guides go over restorative & preventative strategies for people affected by doxing :"
 968  ul_ $ do
 969    li_ $ a_ [href_ "https://crashoverridenetwork.tumblr.com/post/114270394687/so-youve-been-doxed-a-guide-to-best-practices"] "So You’ve Been Doxed - Crash Override Network"
 970    li_ $ a_ [href_ "https://gendersec.tacticaltech.org/wiki/index.php/Self-dox"] "Self-dox - Gender & Tech Resources"
 971    li_ $ a_ [href_ "https://arstechnica.com/information-technology/2015/03/anti-doxing-strategy-or-how-to-avoid-50-qurans-and-287-of-chick-fil-a/"] "Anti-doxing Strategy - Ars Technica"
 972  h2_ "social media"
 973  p_ $ do
 974    "i'd strongly advise looking into safer alternatives ( such as well moderated instances on the "
 975    a_ [href_ "https://www.fediverse.to/"] "fediverse"
 976    " ) , but if that's impractical for whatever reason , this website has a list of comprehensive guides for dealing with harassment , abuse , & privacy settings on mainstream social media sites :"
 977  p_ $ a_ [href_ "https://righttobe.org/guides/how-to-use-social-media-safely/"] "How To Use Social Media Safely - Right To Be"
 978
 979inferiPost :: Post
 980inferiPost = makePost "inferi monologue" "2024-05-20" ["fiction"] $ do
 981  p_ $ do
 982    "a headcannon of how "
 983    a_ [href_ "https://pottermore.fandom.com/wiki/Inferi"] "inferi"
 984    " would greet travelers :"
 985  blockquote_ $ do
 986    p_ "we , stacked like acrobats under the cavern lake surface , writhing in place without sound nor purpose , do cordially invite you & yours to our celebration . we know you’re busy , but spare us a second , & we’ll show you how cozy our waters can be ."
 987    p_ "what ? you don’t want to drown ? do we look like we’re drowning to you ? there’s more of us here than you could ever find up there - don’t you want friends ? you’ll drown in an absence of human contact if you don’t join us soon ."
 988    p_ $ do "i mean , come on . look at us . we have it all . we’re never alone , & we have each other’s backs , in a lattice configuration , & we never have to worry about defectors , because the waters are so "; b_ "cozy"; " ."
 989    p_ "what ? you don’t want to become incorporated into an evil horde ? do we look like an evil horde to you ? we all think you’re a [REDACTED] . you’ve been out of the water so long you’re maladjusted & don’t know right from wrong . i’m confident that even the other , more reasonable land-dwellers would agree with me ."
 990    p_ "you’re on your own , kiddo . we offered our gracious invitation , & you disrespected us . you know , there are those among us who used to be like you . then they joined us , & served their time , & now we’re all one big jolly family . i hope that one day , when you’re one of us like they are , you’ll be thankful we took you under our wing ."
 991    p_ "stop struggling , [REDACTED] ."
 992    p_ $ em_ "we already told you , the waters are cozy ."
 993
 994howIUseComputersPost :: Post
 995howIUseComputersPost = makePost "how i use computers" "2024-08-24"
 996  ["qubesOS", "programming", "haskell", "purescript"] $ do
 997  h2_ "introduction"
 998  p_ $ do
 999    "what follows is an outline of the various hardware & software i currently use for daily computing tasks , in case it's useful for someone else . this is not a prescription of how other people should use technology , nor is it a description of "
1000    link True (fst theOasisPost) "my ideal computing environment"
1001    " ."
1002  todo "explain choices in more detail"
1003  h2_ "hardware"
1004  p_ "if i need to purchase a new device , i prefer to find one that is already used , due to the numerous ethical issues with the production of electronics by large companies ."
1005  p_ $ do
1006    "my primary computer is a Thinkpad T430 running "
1007    a_ [href_ "https://www.qubes-os.org/"] "QubesOS"
1008    " . i chose this laptop because , out of the options that can be flashed with "
1009    a_ [href_ "https://www.coreboot.org/"] "coreboot"
1010    " , it was old enough to be somewhat affordable"
1011    footnote "around 200 USD on Newegg"
1012    " , while still running fast enough for most tasks that don't involve graphics ( & some that do ) ."
1013  p_ "i don't use any peripherals except for a large monitor , to maintain good posture . i have tried mechanical keyboards , but i dislike like how they feel & sound compared to the built-in Thinkpad keyboard . split keyboards are more ergonomic though , so it would be nice to find a split rubber dome keyboard ."
1014  p_ $ do
1015    "my \"smart\"phone is a Pixel 4a"
1016    footnote "this was also around 200 USD on eBay"
1017    " running CalyxOS"
1018    footnote "i am planning to switch to GrapheneOS soon , for the additional security features & ability to use TalkBack , a screen reader . i could not figure out how to enable a screen reader on CalyxOS ."
1019    " ."
1020  h2_ "software"
1021  p_ $ do
1022    "i primarily use CalyxOS for taking notes with "
1023    a_ [href_ "https://f-droid.org/packages/com.orgzly/"] "Orgzly"
1024    " , tracking sleep with "
1025    a_ [href_ "https://f-droid.org/packages/hu.vmiklos.plees_tracker/"] "Plees Tracker"
1026    " , web browsing away from home , insecure communications , & listening to audio-based educational materials ."
1027  p_ "i use QubesOS for web browsing , programming , Serious Writing , & other forms of expression ."
1028  p_ $ do
1029    "i chose QubesOS because i find "
1030    a_ [href_ "https://en.wikipedia.org/wiki/Ambient_authority"] "ambient authority"
1031    " horrifying , but i'm not aware of any robust "
1032    a_ [href_ "https://en.wikipedia.org/wiki/Capability-based_security"] "capability-based"
1033    " operating systems that would fill the same need for me ."
1034  p_ $ do
1035    "QubesOS is slow & "
1036    a_ [href_ "https://github.com/QubesOS/qubes-issues/issues/5907"] "doesn't have screen reader support"
1037    " yet , but it does have some great features like configuration using "
1038    a_ [href_ "https://www.qubes-os.org/doc/salt/"] "Salt"
1039    footnote $ do
1040      "ior maybe even Nix-like configuration in the future if "
1041      a_ [href_ "https://spectrum-os.org/"] "Spectrum"
1042      " goes well"
1043    ", Whonix qubes , & offline qubes . here are a few things i've learned that make it more tolerable :"
1044  ul_ $ do
1045    li_ $ do
1046      "install "
1047      a_ [href_ "https://i3wm.org/"] "i3"
1048    ul_ $ do
1049      li_ $ do
1050        "name qubes using two-character strings so they can be searched for more quickly using "
1051        a_ [href_ "https://wiki.archlinux.org/title/Dmenu"] "dmenu"
1052      li_ "make shortcuts for locking the screen & toggling the status bar"
1053      li_ $ do "make a shortcut ( mine is bound to "; kbd_ "Alt"; " + ";kbd_ "i"; " ) for opening a dmenu list of helper scripts & frequently opened programs"
1054    li_ $ do "use Salt , but not for "; em_ "everything"; " . some tasks are much simpler ior faster with a shell script in dom0 , so i use a top-level shell script to apply salt files & do other things"
1055    li_ $ do
1056      "dark mode can be enabled in "
1057      a_ [href_ "https://xfce.org/"] "XFCE"
1058      " qubes by "
1059      inlineCode "qvm-run"
1060      "ing the following command in each of them :"
1061      code "bash" "xfconf-query -c xsettings -p /Net/ThemeName -s 'Adwaita-dark'"
1062    li_ $ do
1063      "it may not always seem like it at first , but most tasks can be automated from dom0 , including mounting & attaching drives inside qubes . i take advantage of this in a backup script that backs up specific qubes using "
1064      a_ [href_ "https://www.borgbackup.org/"] "BorgBackup"
1065    li_ "i wrote a shell script that copies dotfiles from one qube to the others & applies them , so i only have to clone my dotfiles in one place , but still have my bash aliases , helix config , etc. in every qube"
1066    li_ $ do
1067      a_ [href_ "https://wiki.archlinux.org/title/Rofi"] "rofi"
1068      " can be opened inside a qube using "
1069      inlineCode "qvm-run"
1070      " in dom0 scripts , but the rofi flag "
1071      inlineCode "-normal-window"
1072      " must be added to disable "
1073      inlineCode "override_redirect"
1074      " , which allows the program window to appear undecorated & on top in "
1075      a_ [href_ "https://en.wikipedia.org/wiki/X_Window_System"] "X11"
1076  h2_ "programming"
1077  h3_ "language"
1078  p_ $ do
1079    "currently , the languages i find most useful are "
1080    a_ [href_ "https://www.haskell.org/"] "Haskell"
1081    " & "
1082    a_ [href_ "https://www.purescript.org/"] "PureScript"
1083    " ( which is very similar to Haskell , but compiles to JavaScript ) ."
1084  p_ "a few things i like about them are :"
1085  ul_ $ do
1086    li_ "very clean syntax"
1087    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Pure_function"] "functional purity"
1088    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system"] "Hindley-Milner type system"
1089    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Type_class"] "typeclasses"
1090    li_ $ do
1091      a_ [href_ "https://hoogle.haskell.org/"] "Hoogle"
1092      "/"
1093      a_ [href_ "https://pursuit.purescript.org/"] "Pursuit"
1094    li_ $ do
1095      a_ [href_ "https://github.com/ndmitchell/ghcid"] "ghcid"
1096      "/"
1097      a_ [href_ "https://github.com/kRITZCREEK/pscid"] "pscid"
1098    li_ "high abstraction power"
1099    li_ $ do
1100      "automatically "
1101      a_ [href_ "https://en.wikipedia.org/wiki/Currying"] "curried"
1102      " functions"
1103    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Lazy_evaluation"] "lazy evaluation"
1104    li_ $ do
1105      a_ [href_ "https://en.wikipedia.org/wiki/Row_polymorphism"] "row polymorphism"
1106      " ( PureScript only )"
1107    li_ $ do
1108      "wonderful libraries like "
1109      a_ [href_ "https://github.com/ajnsit/concur"] "Concur"
1110      " , "
1111      a_ [href_ "https://hackage.haskell.org/package/optics"] "optics"
1112      " & "
1113      a_ [href_ "https://hackage.haskell.org/package/apecs"] "apecs"
1114  p_ $ do
1115    "i'm autistic & get overwhelmed by extraneous detail "; em_ "very"; " easily , so i view many of these as accessibility features"
1116    footnote "however , i have heard that for some people , these languages are unpleasant to work with for the some of the same reasons ."
1117    " , because of way they encourage code that is easy to read & reduce the number of things i must keep in working memory at once - while still feeling quite empowering ."
1118  p_ $ do
1119    "for native programs that target embedded devices ior need to be "
1120    a_ [href_ "https://en.wikipedia.org/wiki/Static_build"] "statically built"
1121    " ior "
1122    a_ [href_ "https://en.wikipedia.org/wiki/Cross_compiler"] "cross compiled"
1123    " , "
1124    a_ [href_ "https://www.rust-lang.org/"] "Rust"
1125    " is a nice language that provides a somewhat similar experience to Haskell , while making those tasks much easier ."
1126  p_ $ do
1127    "i've experimented a lot with "
1128    a_ [href_ "https://nixos.org/"] "Nix"
1129    " for provisioning systems & development environments , but i'm hesitant to recommend using it , particularly if , like me , one is significantly constrained by the power of their computer hardware / disk space / network speed , ior are marginalized in some way"
1130    footnote $ do
1131      "i've seen many folks point out that the "
1132      a_ [href_ "https://discourse.nixos.org/"] "Nix forum"
1133      " & subreddit have been particularly ineffectual at fighting bigotry & fascist rhetoric , which has been corroborated by my own experience . from what i can understand , the community has also become a place of heavy conflict after a considerable amount of members were okay with the Nix convention being sponsored by an evil \"defence\" technology company called "
1134      a_ [href_ "https://en.wikipedia.org/wiki/Anduril_Industries"] "Anduril"
1135      " ."
1136    " . i've definitely benefited from Nix before , but those benefits have only barely outweighed the many negatives i've experienced along the way . maybe "
1137    a_ [href_ "https://lix.systems/"] "Lix"
1138    " & "
1139    a_ [href_ "https://auxolotl.org/en/"] "Aux"
1140    " will manage to solve some of those issues ."
1141  h3_ "code editor"
1142  p_ $ do
1143    "the code editor i use most often is "
1144    a_ [href_ "https://helix-editor.com/"] "helix"; " ."
1145  p_ $ do
1146    "my first editor was Sublime Text , then VSCodium , then Atom , & i finally found an editor i liked when i tried vim & realized i could mostly avoid using the mouse"
1147    footnote "using a mouse as an input device is fine , & i wish development software would use it more often . my issue is the precision required in most mouse-driven interfaces . precise clicks are difficult for me, & it would be much more accessible if elements that have a larger hitbox ( like radial menus ) were used more often ."
1148    " . once i was proficient with vim , i moved on to "
1149    a_ [href_ "https://github.com/doomemacs/doomemacs"] "Doom Emacs"
1150    " ."
1151  p_ "Emacs has a lot going for it , like radical introspection & extensibility , but it was just too slow for me . the language servers for Haskell & PureScript would often crash my qube because of how much memory they used in Emacs . i also disliked dealing with the Doom abstraction , but when i tried going without it , maintaining a complex configuration was too time consuming ."
1152  p_ "so , i switched to helix . it is rarely too slow for me , even in a qube with 4GB of RAM . it hardly requires any configuration to match my Doom Emacs setup . after learning them properly , i've also grown to appreciate the Kakoune-inspired keybindings more than vim's ." 
1153
1154unlicensePost :: Post
1155unlicensePost = makePost "why i use the Unlicense & CC0" "2024-07-21"
1156  ["anticopyright", "philosophy"] $ do
1157  h2_ "introduction"
1158  p_ $ do
1159    "i don't know that much about the specifics of copyright law ; this post is more of a philosophical exploration of the concept of \"intellectual property\" in general"
1160    footnote "it's also not a comprehensive argument , as there are quite a few reasons i dislike \"intellectual property\" that aren't mentioned here ."
1161    " ."
1162  h2_ "\"intellectual property\" relies on death"
1163  p_ "what would \"perfect\" copyright enforcement look like ? extrapolating what i see as the current system’s telos , it would look like protected \"intellectual property\" being physically impossible to create for everyone in the universe except for the \"owner\" ."
1164  p_ "every time new \"intellectual property\" is created , everyone else’s agency decreases by some amount . it is a system reliant on death , because if death were abolished , all of the most important expressions would get copyrighted eventually , & never expire , rendering most people born after that period unable to express much ."
1165  p_ "so , if copyright decreases everyone’s agency , uses death as a mechanism to redistribute temporal privilege , & relies on the distributed violence of the state to enforce all of this , what’s the alternative ?"
1166  h2_ "copyleft & copyfarleft"
1167  p_ "efforts to use the system against itself , like copyleft & copyfarleft , are admirable in terms of what they aim to achieve . they can stop people who use one's work from restricting others as much as they would have otherwise ."
1168  p_ "but if one has the option to forgo copyright entirely , i’m not convinced that copyleft is preferable . it still relies on the copyright system in a significant way . why rely on an inefficient system that harms others , when one could oppose harmful usage of their work through other channels ?"
1169  p_ "some people claim that once one has given up their \"intellectual property\" , they have no \"right\" to oppose anyone’s usage of it . this would only make sense if the legal system were the only way to stop harm ."
1170  p_ "as an example , if somebody used a painting i made in a widely shared fascist propaganda video without permission , there are numerous reasons why this is bad that are not \"they stole my 'intellectual property'\" . there are also countless ways to counteract this other than to wait around for the copyright system to punish them in a way that isn’t even responding directly to what was wrong about what they did in the first place ."
1171  h2_ "even copyleft causes psychic damage"
1172  p_ "another unfortunate side effect of copyleft’s strategy is that , similar to permissive licenses , even users of copyleft software that are trying their best to make their derivatives freely available have to worry about the chance that they misunderstand something & get punished by the legal system ."
1173  p_ $ do
1174    "anecdotally , trying to make sense of the rules for using copyleft software"
1175    footnote "which is an instance of trying to navigate the legal system"
1176    " was very difficult & stressful for me . i have a particularly low tolerance for that kind of thing , but i doubt that this is an uncommon experience more generally , at least to a lesser extent ."
1177  p_ "in contrast , i don't have those issues when i’m using public domain software , because there aren't so many rules to keep track of ."
1178  h2_ "releasing work into the public domain"
1179  p_ $ do
1180    "regarding anticopyright praxis , my current preference is to use the "
1181    a_ [href_ "https://unlicense.org/"] "Unlicense"
1182    " for code , & "
1183    a_ [href_ "https://creativecommons.org/public-domain/cc0/"] "CC0"
1184    " for everything else ."
1185  p_ "one could also release work without a license notice at all , if they felt like their audience could trust them enough to not do anything about \"infringements\" ."
1186
1187veganTipsPost :: Post
1188veganTipsPost = makePost "a few vegan tips" "2024-07-03"
1189  ["vegan", "guide"] $ do
1190  h2_ "introduction"
1191  p_ "here are a few things i’ve learned since becoming vegan that weren’t immediately obvious , ior aren’t discussed as often by vegans because they’re about keeping insects ior wild animals safe ."
1192  p_ "i'm not an expert on animal welfare , so i may be mistaken about some of these . but i still think it's worth sharing the little ways we can avoid harming other living beings in everyday life ."
1193  h2_ "the tips"
1194  ul_ $ do
1195    li_ "be careful with stickers & sticky things . bugs can become trapped on them easily"
1196    li_ $ do "be "; em_ "very"; " distrustful of unfamiliar non-vegans’ assessments of whether things are vegan eor not"
1197    li_ $ do
1198      "if bugs get trapped in the bathtub frequently , leaving a long strip of toilet paper draped into the tub when it's not in use can function as an escape route for them to climb out on"
1199      footnote "i almost never see bugs trapped in the tub anymore after doing this"
1200    li_ $ do
1201      "if one shares a home with rodents , there are effective methods to keep them out of living spaces without harming them , like sealing gaps in the home with steel wool , & being very careful to keep any traces of food tidy & well sealed . "
1202      todo "link to a detailed guide"
1203    li_ "don’t assume that random household items like toilet paper are vegan by default"
1204    li_ $ do
1205      "fireworks are "
1206      a_ [href_ "https://www.animal-ethics.org/how-fireworks-harm-nonhuman-animals/"] "harmful ior deadly"
1207      " to many living things"
1208    li_ "avoid unnecessary loud noises that could distress nearby animals . some music may be an exception , though , if it's not too loud"
1209    li_ "don’t walk when the ground is unlit to avoid crushing bugs"
1210    li_ $ do "reduce ior eliminate reliance on car travel for "; em_ "many"; " reasons"
1211    li_ "recreational fires often burn insects alive & wood smoke can be harmful to animals"
1212    li_ "avoid planting poisonous plants where wild animals might eat them"
1213  h2_ "related writing"
1214  p_ $ do
1215    "if one's interested in more practical advice of this nature , i can recommend the writings of a researcher named "
1216    a_ [href_ "https://briantomasik.com/"] "Brian Tomasik"
1217    " . while i often disagree with his politics & conclusions on many topics , as far as i can tell , he is earnestly trying to reduce the harm caused to wild animals & insects ( among others ) more than most people i’ve encountered . he also documents his ideas in a very comprehensive & accessible way ."
1218
1219wallOfShardsPost :: Post
1220wallOfShardsPost = makePost "wall of shards" "2024-05-10"
1221  ["fiction"] $ do
1222  h2_ "loading world ..."
1223  p_ $ do
1224    "type "; inlineCode "start"
1225    " to collect a shard ."
1226  code "haskell" "start"
1227  p_ $ em_ "shard count : 2"
1228  h2_ "your lab"
1229  p_ "you're sitting in your office chair . all of your research tools are systematically organized around you , with your computer terminal at the center of it all . your friend b6e is sitting here , staring at one of the photos on your wall , although you can't tell which one ."
1230  code "haskell" "talkTo b6e"
1231  p_ "you break the silence ."
1232  blockquote_ "are you ready to get started ?"
1233  blockquote_ "yeah . what will it feel like , exactly ? like will i even notice you're there ?"
1234  blockquote_ "well , i'm not completely sure , but it will probably be similar to what it feels like to focus on your thoughts in one of those terrible dining areas with televisions all over . difficult , & a struggle to not zone out & succumb to the noise ."
1235  blockquote_ "too bad i can't take a nap during it ."
1236  blockquote_ "i know right . well , if you're ready then have a seat & i'll start projecting ."
1237  p_ "she sits down & puts on the acrylic beanie . all of the cables dangling off of it match her hair color , which gives the illusion that her hair extends onto the floor ."
1238  code "haskell" "use Terminal"
1239  p_ $ do
1240    "you swivel over to your terminal & execute "
1241    inlineCode "1ntr0j3ct.hs"; " ."
1242  blockquote_ "you named the script using leetspeak ?"
1243  blockquote_ "yep , it makes words taste better ."
1244  p_ "you get up & lie down on your yoga mat . your vision starts to fade ..."
1245  h2_ "the center"
1246  p_ "a sea of tall , green grass stretches endlessly in all directions except to the west , where a short , smoothly vertical cliff face wraps a plateau . you're sitting at the junction of a boardwalk splitting off to the north , south , & east ."
1247  p_ "you're not sure why , but this location feels like the central point in her mind , where all other synapse clusters meet . why then is it so barren ?"
1248  p_ "it's dark , but the moon is bright enough that you can see sheets of rain sweeping the expanse . the rain & wind produce a soothing noise that ripples in waves off of the grass ."
1249  p_ "even farther in the distance , you hear another ceaseless noise coming from all directions except up & down . it's a harsh , glass grinding static , but not quite painful because of how faint it is ."
1250  code "haskell" "go West"
1251  p_ "there is a cliff face to the west . you hover up to the top edge ."
1252  h2_ "eastern plateau ridge"
1253  p_ "you aren't that much higher than the boardwalks below , but you can see much farther into the distance from up here . there is a sliver of deep crimson shimmer spanning the entire horizon . maybe that's what's making that grinding noise ?"
1254  p_ "the footpath you're on continues to the north & south . to the west , the top of the plateau slopes downward toward a pool of glowing purple liquid . the pool appears to converge & warp in upon itself , almost as if a tiny black hole were floating a few meters above the surface ."
1255  p_ "there's a rectangular slot in the ground close by , but you can't see what's in it from here ."
1256  code "haskell" "examine Slot"
1257  h2_ "slot machine"
1258  p_ "now that you're closer to the slot , you notice a hand-written sign next to it that says :"
1259  blockquote_ "slot machine : enter the slot & slide like a coin to a random location"
1260  p_ "somebody walks over to you & asks what you're doing ."
1261  blockquote_ "hello ! i'm trying to figure out what the deal with this slot thing is ."
1262  blockquote_ "no you're not . i saw you over there trying to vandalize my hand-written sign ."
1263  blockquote_ "you what ? i didn't even touch the sign ."
1264  blockquote_ "then how did all of those purple stains get on it ?"
1265  p_ "you glance at the sign again . it does appear more stained than you remember it . you wonder , aloud :"
1266  blockquote_ "maybe it's from that pool over there ? whatever gravity well is swirling above the pool might have become volatile ."
1267  blockquote_ "that's certainly a possiblity . but it seems much more likely that you hated my handwriting style & wanted to destroy it yourself ."
1268  blockquote_ "you serious ? i didn't even think of the handwriting style until now. it was perfectly serviceable ."
1269  blockquote_ "oh ."
1270  p_ "they gaze into the distance uncomfortably ."
1271  p_ "the grinding sound grows louder , & the crimson glow brighter ."
1272  p_ $ em_ "shard count : 3"
1273  code "haskell" "ask SignPainter . about Sound"
1274  p_ "you ask the sign painter if they know where that sound is coming from ."
1275  blockquote_ "no ."
1276  code "haskell" "ask SignPainter . about CrimsonGlow"
1277  p_ "you ask them if they know what the crimson glow is coming from ."
1278  blockquote_ "no ."
1279  p_ "the sign painter sits on the ground , placing their legs into the slot . they carefully lower the rest of their body into the slot & let go. a few seconds later the sound of them sliding fades away completely ."
1280  p_ "it starts to rain . the droplets are purple , & stains your grey dress ."
1281  code "haskell" "go East"
1282  p_ "you hover back down the cliff face & land on the boardwalk again ."
1283  h2_ "the center"
1284  p_ "it feels nice to return to the center . that plateau to the west had an unpleasant vibe . the boardwalk continues to the north , south , & east ."
1285  code "haskell" "go West"
1286  p_ "you look back at the cliff face . a voice in your head says it may be a good idea to return to the slot machine , because it could spit you out where you need to go much faster than hovering there yourself ."
1287  p_ "but no . that would be a gamble ."
1288  p_ "you don't like leaving things to chance ."
1289  code "haskell" "go North"
1290  p_ "with your mind made up , you hover northward along the boardwalk ."
1291  h2_ "an expanse"
1292  p_ "you've reached the end of the boardwalk . this location is so far from the plateau that it's barely visible through the rain . the crimson glow to the north stretches higher into the sky here . it's like the source of the grinding noise is a wall in the distance that wraps around the boundary of b6e's mind ."
1293  p_ "there is a swarm of winged , metal centipedes flying over your head ."
1294  p_ "the grinding sound is loud enough here to put you on edge ."
1295  p_ $ em_ "shard count : 4"
1296  code "haskell" "ask Centipede . about Sound"
1297  p_ "you call out to the centipedes above you ."
1298  blockquote_ "excuse me , would any of you be able to help me answer a quick question ?"
1299  p_ "one of them lands in front of you . they are wearing a really cool sweater with a fractal pattern knitted onto it ."
1300  blockquote_ "i might be able to help ya ! what's ur question ?"
1301  blockquote_ "oh nice ! thank you . do you know what that glass grinding noise in the distance is ?"
1302  blockquote_ "ya mean death ?"
1303  blockquote_ "what ?"
1304  blockquote_ "death . like the thing that happens when ya have a shard count of 11 ? looks like ya have 4 shards . ya might wanna stop thinkin' 'bout the wall !! you'll get less shards that way !"
1305  p_ "the other centipedes start gliding to the southeast ."
1306  blockquote_ "that's what the shard count is about ? why would thinking about the wall give you more shards ? by the wall you mean that crimson thing in the distance , right ?"
1307  blockquote_ "hey kid i'm sorry but i gotta go my friends are leavin' & i don't wanna get left behind but i'm sure someone else can help ya out ! althoughhh a lotta people might be hesitant ( ior worse ) to talk about the shards . not sure what to tell ya 'bout that ."
1308  blockquote_ "ok , well thanks for the info . i'll keep searching then ."
1309  blockquote_ "be careful ."
1310  p_ "they fly away ."
1311  code "haskell" "save"
1312  p_ $ em_ "serializing timeline ..."
1313  p_ $ em_ "game saved ."
1314  p_ $ em_ $ do
1315    "enter the code "
1316    inlineCode "5nJ0kL3-001"
1317    " to restore the game to this point in time ."
1318  code "haskell" "pop"

Zoxuli.hs

raw file

  1module Zoxuli
  2  ( Zoxuli
  3  , PassData(..), PassOne(..), PassTwo(..)
  4  , slugify
  5  , rawHtml
  6  , Link(..), link, linkToAnchor
  7  , Code(..), code, inlineCode
  8  , h1_, h2_, h3_, h4_, h5_, h6_
  9  , toc
 10  , Footnote(..), footnote
 11  , Page(..), makePage
 12  , Project, ProjectMeta(..), makeProject
 13  , Post, PostMeta(..), makePost
 14  , reverseChronological
 15  , SrcFile(..)
 16  ) where
 17
 18import Lucid hiding (h2_, h3_, h4_, h5_, h6_)
 19import qualified Lucid as L
 20import Lucid.Base (TermRaw, termRaw)
 21import Optics
 22import GHC.Generics (Generic)
 23import Control.Monad (when)
 24import Control.Monad.State.Lazy (State, get, modify, lift)
 25import Data.Text (Text, replace, toLower, pack, unpack)
 26import Data.List (sortBy)
 27
 28type Zoxuli = HtmlT (State PassData) ()
 29
 30data PassData = One PassOne | Two PassTwo
 31  deriving (Generic)
 32
 33data PassOne = PassOne
 34  { links     :: [Link]
 35  , headers   :: [Header]
 36  , footnotes :: [Zoxuli]
 37  , codes     :: [Code] } deriving (Generic)
 38
 39data PassTwo = PassTwo
 40  { backlinks     :: [Link]
 41  , headers       :: [Header]
 42  , footnotes     :: [Footnote]
 43  , footnoteIndex :: Int
 44  , codes         :: [Text]
 45  , srcFiles      :: [SrcFile] } deriving (Generic)
 46
 47data Link = Link
 48  { body             :: Text
 49  , destinationTitle :: Text
 50  , destinationPath  :: Text }
 51
 52link :: Bool -> Page -> Text -> Zoxuli
 53link createBacklink page =
 54  link' createBacklink page Nothing
 55
 56linkToAnchor :: Bool -> Page -> Text -> Text -> Zoxuli
 57linkToAnchor createBacklink page anchor =
 58  link' createBacklink page (Just anchor)
 59
 60link' :: Bool -> Page -> Maybe Text -> Text -> Zoxuli
 61link' createBacklink page anchor body = do
 62  when createBacklink $ do
 63    let newLink = [Link body page.title page.path]
 64    lift . modify $ #_One % #links %~ (<> newLink)
 65  a_ [ href_ $ "/" <> page.path <> ".html"
 66         <> maybe "" ("#" <>) anchor
 67     , id_ $ slugify body ]
 68    $ toHtml body
 69
 70data Code = Code
 71  { lang :: Text
 72  , body :: Text }
 73
 74code :: Text -> Text -> Zoxuli
 75code lang body = lift get >>= \case
 76  One _ -> lift . modify $ #_One % #codes %~ (<> newCode)
 77  Two s -> case s.codes of
 78    (c:rest) -> do
 79      rawHtml c
 80      lift . modify $ #_Two % #codes .~ rest
 81    _ -> pure ()
 82  where newCode = [Code lang body]
 83
 84inlineCode :: Zoxuli -> Zoxuli
 85inlineCode = code_ [class_ "verbatim"]
 86
 87data Header = Header
 88  { level :: Int
 89  , body  :: Text } deriving (Generic)
 90
 91hx_ :: Int -> (Zoxuli -> Zoxuli) -> Text -> Zoxuli
 92hx_ level headerFunction body = do
 93  let newHeader = [Header level body]
 94  lift . modify $ #_One % #headers %~ (<> newHeader)
 95  headerFunction $ do
 96    a_ [ href_ $ "#" <> slugify body
 97       , id_ $ slugify body ]
 98       $ toHtml body
 99
100h2_ :: Text -> Zoxuli
101h2_ = hx_ 2 L.h2_
102
103h3_ :: Text -> Zoxuli
104h3_ = hx_ 3 L.h3_
105
106h4_ :: Text -> Zoxuli
107h4_ = hx_ 4 L.h4_
108
109h5_ :: Text -> Zoxuli
110h5_ = hx_ 5 L.h5_
111
112h6_ :: Text -> Zoxuli
113h6_ = hx_ 6 L.h6_
114
115toc :: Zoxuli
116toc = lift get <&> (^? #_Two % #headers) >>= \case
117  Just headers@(_:_) -> details_ [class_ "toc"] $ do
118    summary_ "table of contents"
119    ol_ $ tocList headers
120  _ -> pure ()
121
122tocList :: [Header] -> Zoxuli
123tocList [] = pure ()
124tocList ((Header level body) : rest) = do
125  let (nested, following) =
126        break (\h -> h.level == level) rest
127  li_ . a_ [ href_ $ "#" <> slugify body ] $ toHtml body
128  if null nested then pure () else ol_ $ tocList nested
129  tocList following
130
131data Footnote = Footnote Int Zoxuli
132
133footnote :: Zoxuli -> Zoxuli
134footnote content = lift get >>= \case
135  One _ -> lift . modify $ #_One % #footnotes %~ (<> [content])
136  Two s -> do
137    sup_ .
138      a_ [ href_ $ "#fn" <> pack (show s.footnoteIndex)
139         , id_ $ "r" <> pack (show s.footnoteIndex) ]
140        $ do "["; toHtml $ show s.footnoteIndex; "]"
141    lift . modify $ #_Two % #footnoteIndex %~ (+ 1)
142
143slugify :: Text -> Text
144slugify = replace " " "-" . toLower
145
146rawHtml :: TermRaw arg result => arg -> result
147rawHtml = termRaw "div"
148
149data Page = Page
150  { title :: Text
151  , path  :: Text
152  , tags  :: [Text]
153  , html  :: Zoxuli } deriving (Generic)
154
155instance Eq Page where
156  a == b = a.path == b.path
157
158makePage :: Text -> Zoxuli -> Page
159makePage title = Page title path []
160  where path = case title of
161          "home" -> "index"
162          _ -> slugify title
163
164type Project = (Page, ProjectMeta)
165
166data ProjectMeta = ProjectMeta
167  { description :: Text
168  , repo :: Text } deriving (Generic)
169
170makeProject :: Text -> Text -> Text -> [Text] -> Zoxuli -> Project
171makeProject title description repo tags html =
172  (Page title path tags html, ProjectMeta description repo)
173  where path = "projects/" <> slugify title
174
175type Post = (Page, PostMeta)
176
177data PostMeta = PostMeta
178  { date   :: Text
179  , author :: Text } deriving (Generic)
180
181makePost :: Text -> Text -> [Text] -> Zoxuli -> Post
182makePost title date tags html =
183  (Page title path tags html, PostMeta date "eevie")
184  where path = "posts/" <> slugify title
185
186reverseChronological :: [Post] -> [Post]
187reverseChronological = sortBy comp
188  where comp a b = getInt b `compare` getInt a
189        getInt :: Post -> Int
190        getInt p = read $ unpack $ replace "-" "" $ p ^. _2 % #date
191
192data SrcFile = SrcFile
193  { name    :: Text
194  , content :: Text }

Main.hs

raw file

  1module Main (main) where
  2
  3import Zoxuli
  4import Pages
  5import Lucid hiding (h2_, h3_, h4_, h5_, h6_)
  6import Optics
  7import System.Directory (createDirectoryIfMissing)
  8import System.FilePath ((</>))
  9import System.Process (spawnCommand, readProcess)
 10import Control.Monad (void)
 11import Control.Monad.State.Lazy (evalState, execState)
 12import Data.Maybe (fromMaybe)
 13import Data.Text (Text, unpack, pack)
 14import Data.Foldable (foldrM)
 15import Data.Map (Map)
 16import qualified Data.Map as Map
 17import qualified Data.ByteString.Lazy as BS
 18
 19-- | page rendering and writing
 20
 21main :: IO ()
 22main = do
 23  createBuildDirectories
 24  copyStaticFiles
 25  copySrcFiles
 26  writePages =<< getSrcFiles
 27  createArchive
 28
 29buildDir :: FilePath
 30buildDir = "site"
 31
 32createBuildDirectories :: IO ()
 33createBuildDirectories =
 34  mapM_ (createDirectoryIfMissing True)
 35    [ buildDir
 36    , buildDir </> "projects"
 37    , buildDir </> "posts"
 38    , buildDir </> "tags" ]
 39
 40copyStaticFiles :: IO ()
 41copyStaticFiles = void $ spawnCommand $ "cp -r static/* " <> buildDir
 42
 43copySrcFiles :: IO ()
 44copySrcFiles = void $ spawnCommand $ "cp -r app " <> buildDir <> "/src"
 45
 46getSrcFiles :: IO [SrcFile]
 47getSrcFiles = do
 48  let getFileContents name = do
 49        content <- readFile $ "app" </> name
 50        highlightCode $ Code "haskell" $ pack content
 51      srcFileNames = ["Pages.hs", "Zoxuli.hs", "Main.hs"]
 52  srcFileContents <- mapM getFileContents srcFileNames
 53  pure $ zipWith SrcFile (pack <$> srcFileNames) srcFileContents
 54
 55writePages :: [SrcFile] -> IO ()
 56writePages srcFiles = do
 57  passTwoData <- foldrM runPassOne Map.empty allPages
 58  let passTwoData' = passTwoData <&> (#_Two % #srcFiles .~ srcFiles)
 59  mapM_ (\p -> writePage p.path $ runPassTwo passTwoData' p)
 60    allPages
 61
 62allPages :: [Page]
 63allPages = pageWrapper False bookPage :
 64  (pageWrapper True <$> (
 65    mainPages
 66    <> (fst <$> projectPages)
 67    <> (fst <$> postPages)
 68    <> tagPages ))
 69
 70runPassOne :: Page -> Map Text PassData -> IO (Map Text PassData)
 71runPassOne page acc =
 72  let passOneData = execState
 73        (renderBST page.html) (One (PassOne [] [] [] [])) in
 74  case passOneData of
 75    One (PassOne links headers footnotes codes) -> do
 76      codes' <- mapM highlightCode codes
 77      let acc' = Map.alter fromPassOne page.title acc
 78          footnotes' = zipWith Footnote [1..] footnotes
 79          fromPassOne (Just (Two d)) =
 80            Just . Two $ d & #headers   .~ headers
 81                           & #footnotes .~ footnotes'
 82                           & #codes     .~ codes'
 83          fromPassOne _ = Just . Two $
 84            PassTwo [] headers footnotes' 1 codes' []
 85      pure $ foldr (makeBacklinks page) acc' links
 86    _ -> pure acc
 87
 88-- the theme css is generated with the following command:
 89-- `chroma --html-styles --style "algol_nu"`
 90highlightCode :: Code -> IO Text
 91highlightCode c =
 92  pack <$> readProcess "chroma"
 93    [ "--html"
 94    , "--html-only"  -- only return code fragment
 95    , "--html-lines" -- include line numbers
 96    , "--lexer", unpack c.lang ]
 97    (unpack c.body)
 98
 99makeBacklinks
100  :: Page -> Link -> Map Text PassData -> Map Text PassData
101makeBacklinks page ln backlinks =
102  if page.title == "book" then backlinks
103  else Map.alter addBacklink ln.destinationTitle backlinks
104  where backlink = Link ln.body page.title page.path
105        addBacklink (Just (Two d)) =
106          Just . Two $ d & #backlinks %~ cons backlink
107        addBacklink _ = Just . Two $
108          PassTwo [backlink] [] [] 1 [] []
109
110runPassTwo :: Map Text PassData -> Page -> BS.ByteString
111runPassTwo passOneData page =
112  evalState (renderBST page.html) pageData
113  where pageData = fromMaybe (Two (PassTwo [] [] [] 1 [] [])) $
114          Map.lookup page.title passOneData
115
116writePage :: Text -> BS.ByteString -> IO ()
117writePage path = BS.writeFile (buildDir </> unpack path <> ".html")
118
119createArchive :: IO ()
120createArchive = void $ spawnCommand
121  $ "cd " <> buildDir <> " && tar -czvf eevie.dev.tar.gz *"