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      "macroquad.js is under the "
 181      a_ [href_ "https://github.com/not-fl3/macroquad/blob/master/LICENSE-MIT"] "MIT license"
 182      " license"
 183    li_ $ do
 184      a_ [href_ "https://www.dafont.com/offenbacher-reform-cat.font"] "Offenbacher Reform"
 185      " by Peter Wiegel is under the "
 186      a_ [href_ "https://openfontlicense.org/"] "SIL Open Font License"
 187
 188downloadPage :: Page
 189downloadPage = makePage "download" $ do
 190  h2_ "archive"
 191  p_ "a full archive of this website is available for offline viewing :" 
 192  p_ $ a_ [href_ "/eevie.dev.tar.gz"] "download archive"
 193  h2_ "book"
 194  p_ $ do
 195    "there is a book page containing every post , designed for printing ior saving as a PDF :"
 196  p_ $ link False bookPage "view book"
 197  h2_ "source"
 198  p_ "one can clone the source repository of this site using git :"
 199  code "bash" "git clone https://git.sr.ht/~nebulae/eevie.dev"
 200
 201projectsPage :: Page
 202projectsPage = makePage "projects" $ do
 203  h2_ "art"
 204  ul_ $ do
 205    li_ $ link True digitalArtPage "digital"
 206    li_ $ a_ [href_ "https://plokta.bandcamp.com/"] "music"
 207    li_ $ link True photographyPage "photography"
 208  h2_ "software"
 209  ul_ $ do
 210    mapM_ (li_ . projectPreview) projectPages
 211    li_ $ do
 212      b_ $ a_ [href_ "yarnstar/index.html"] "yarnstar"
 213      " - an implementation of Nova in Elm"
 214    li_ $ do
 215      b_ $ a_ [href_ "https://git.sr.ht/~nebulae/concur_vite"] "Concur + Vite"
 216      " - a purescript-concur starter template"
 217    li_ $ do
 218      b_ $ a_ [href_ "miraquad/index.html"] "[WIP] Miraquad"
 219      " - create 2D games in Mira"
 220    li_ $ do
 221      b_ $ a_ [href_ "https://git.sr.ht/~nebulae/nova_conjure"] "[WIP] NovaConjure"
 222      " - interactive fiction witchcraft in Nova"
 223    li_ $ do
 224      b_ $ a_ [href_ "https://git.sr.ht/~nebulae/antibubble"] "[WIP] Antibubble"
 225      " - nested bubble world exploration game"
 226
 227postsPage :: Page
 228postsPage = makePage "posts" $ do
 229  p_ "in which i externalize the technicolor collapse of techno-synaptic maps of mind matter i’m made of ."
 230  ul_ $ mapM_ (li_ . postPreview) postPages
 231  microblog
 232
 233microblog :: Zoxuli
 234microblog = do
 235  h2_ "microblog"
 236  p_ "for shorter thoughts !"
 237  micropost "2024-12-06" $ do
 238    "low back creaking"; br_ []
 239    "dizzy when standing"; br_ []
 240    "both eyes blurring"; br_ []
 241    "can't we learn while moving ?"
 242  micropost "2024-06-09" $ do
 243    "i hate the term "
 244    a_ [href_ "https://en.wikipedia.org/wiki/Overfishing"] "\"overfishing\""
 245    " , because it implies that there’s an acceptable amount of "
 246    a_ [href_ "https://animalequality.org/issues/fish/"] "fishing"
 247    " ."
 248  where micropost :: Text -> Zoxuli -> Zoxuli
 249        micropost date content =
 250          p_ $ div_ [class_ "border"] $ do
 251            p_ $ b_ $ toHtml date
 252            p_ content
 253
 254bookPage :: Page
 255bookPage = makePage "book" $ do
 256  h2_ "chapters"
 257  ol_ $ mapM_ (li_ . toHtml . (^. #title)) pages
 258  mapM_ chapter pages
 259  where pages :: [Page]
 260        pages = (fst <$> postPages') <> (fst <$> projectPages')
 261        chapter :: Page -> Zoxuli
 262        chapter page = do
 263          hr_ []
 264          h1_ $ toHtml page.title
 265          page.html
 266
 267tagsPage :: Page
 268tagsPage = makePage "tags" $ do
 269  p_ "hypertext flowed through the cracks in the ground , illuminated by ambient amethyst light from the sky above ."
 270  ul_ $ mapM_ (li_ . tagPreview) allTags
 271
 272seriesPage :: Page
 273seriesPage = makePage "series" $ do
 274  mapM_ renderSeries allSeries 
 275
 276sourcePage :: Page
 277sourcePage = makePage "source" $ do
 278  p_ $ do
 279    "this page contains all of the "
 280    linkToAnchor True (fst howIUseComputersPost) "language" "Haskell"
 281    " code that's used to generate this website . the full source repository is on "
 282    a_ [href_ "https://git.sr.ht/~nebulae/eevie.dev"]
 283      "SourceHut"
 284    " . i've also written about the "
 285    link True (fst zoxuliProject) "story behind this design"
 286    " . "
 287  lift get <&> (^? #_Two % #srcFiles) >>= \case
 288    Just srcFiles -> mapM_ renderSrcFile srcFiles
 289    _             -> pure ()
 290  where renderSrcFile :: SrcFile -> Zoxuli
 291        renderSrcFile f = do
 292          h2_ f.name
 293          p_ $ a_ [href_ $ "/src/" <> f.name] "raw file"
 294          rawHtml f.content
 295
 296contactPage :: Page
 297contactPage = makePage "contact" $ do
 298  p_ $ do
 299    "email : " 
 300    span_ [class_ "email"] $ do
 301      "eevie@sent"; b_ "obfuscation-text"; ".com"
 302  h2_ "don't contact me if ..."
 303  p_ $ do "if you "; em_ "intentionally"; " propagate any of the following , i do "; em_ "not"; " want to hear from you :"
 304  ul_ $ do
 305    li_ "fascism"
 306    li_ "nationalism"
 307    li_ "classism"
 308    li_ "speciesism"
 309    li_ "substratism"
 310    li_ "ageism"
 311    li_ "antisemitism"
 312    li_ "racism"
 313    li_ "colorism"
 314    li_ "sexism"
 315    li_ "deathism"
 316    li_ "ableism"
 317    li_ "sanism"
 318    li_ "transphobia"
 319    li_ "enbyphobia"
 320    li_ "interphobia"
 321    li_ "aphobia"
 322    li_ "lesbophobia"
 323    li_ "homophobia"
 324    li_ "biphobia"
 325    li_ "fatphobia"
 326    li_ "pluralphobia"
 327  todo "create a PGP key"
 328
 329todo :: Text -> Zoxuli
 330todo title = link True todoPage $ "todo : " <> title
 331
 332todoPage :: Page
 333todoPage = makePage "todo" $ do
 334  ul_ $ do
 335    li_ $ do
 336      "follow Seirdy's "
 337      a_ [href_ "https://seirdy.one/posts/2020/11/23/website-best-practices/"] "best practices for inclusive textual websites"
 338    li_ $ do
 339      "make an "; inlineCode "md"; " function that uses the "
 340      a_ [href_ "https://hackage.haskell.org/package/cmark"] "cmark"
 341      " library"
 342    li_ "RSS/Atom feeds"
 343    li_ "N24 manager , swirlsaber , particle art , & helix lavender theme" 
 344  p_ "any todos across the site are linked to this page , so you can view them in the list of backlinks :"
 345
 346photo :: Text -> Text -> Zoxuli
 347photo file alt = img_
 348  [ src_ $ "/img/" <> file
 349  , style_ "width: 100%"
 350  , alt_ alt ]
 351
 352digitalArtPage :: Page
 353digitalArtPage = makePage "digital art" $ do
 354  h2_ "trans hacker fæ"
 355  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."
 356  p_ $ do
 357    "( "
 358    a_ [href_ "/img/trans-hacker-fae.jpg"] "version without overlay"
 359    " )"
 360  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 ."
 361  p_ $ do
 362    "made using "
 363    a_ [href_ "https://www.blender.org/"] "Blender"
 364    " , "
 365    a_ [href_ "https://godotengine.org/"] "Godot"
 366    " , & "
 367    a_ [href_ "https://krita.org/en/"] "Krita"
 368    " ."
 369  h2_ "spiky swamp"
 370  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."
 371  p_ "this is a screenshot from my first attempt at making a low-poly 3D game ."
 372
 373photographyPage :: Page
 374photographyPage = makePage "photography" $ do
 375  photo "banner.png" "a dithered photograph of blue & pink flowers"
 376  photo "plants5.jpg" "a beetle resting on the center of a fully bloomed flower"
 377  photo "plants4.jpg" "a small butterfly in a bright field of flowers"
 378  photo "plants1.jpg" "a brightly lit plant with bundles of fuzzy pink flowers"
 379  photo "plants2.jpg" "a bundle of fuzzy white flowers in a verdant garden"
 380  photo "plants3.jpg" "stalks of bamboo in a forest with large bokeh in the background"
 381
 382-- | tag pages
 383
 384taggedPages :: [Page]
 385taggedPages = (fst <$> projectPages) <> (fst <$> postPages)
 386
 387allTags :: [Text]
 388allTags = deduplicate $ concatMap (^. #tags) taggedPages
 389  where deduplicate = Set.toList . Set.fromList
 390
 391pagesWithTag :: Text -> [Page]
 392pagesWithTag tag = filter (elem tag . (^. #tags)) taggedPages
 393
 394tagPreview :: Text -> Zoxuli
 395tagPreview tag = do
 396  toHtml . show . length $ pagesWithTag tag; " - "
 397  a_ [href_ $ "/tags/" <> slugify tag <> ".html"] $ toHtml tag
 398
 399renderTags :: [Text] -> Zoxuli
 400renderTags tags =
 401  sequence_ . intersperse " , " $ renderTag <$> tags
 402  where renderTag :: Text -> Zoxuli
 403        renderTag tag =
 404          a_ [ href_ $ "/tags/" <> slugify tag <> ".html" ]
 405            $ toHtml tag
 406
 407tagPages :: [Page]
 408tagPages = makeTagPage <$> allTags
 409
 410makeTagPage :: Text -> Page
 411makeTagPage tag = Page tag path [] html
 412  where path = "tags/" <> slugify tag
 413        html = do
 414          p_ $ do "pages tagged \""; toHtml tag; "\":"
 415          ul_ . mapM_ (\p -> li_ $ link False p p.title)
 416            $ pagesWithTag tag
 417
 418-- | page series
 419
 420data Series = Series
 421  { name  :: Text
 422  , pages :: [Page] } deriving (Generic)
 423
 424allSeries :: [Series]
 425allSeries = [computingSeries]
 426
 427computingSeries :: Series
 428computingSeries = Series "liberatory computing"
 429  [ fst theOasisPost
 430  , fst unlicensePost
 431  , fst timelineSplittingPost
 432  , fst queerCybersecPost
 433  , fst howIUseComputersPost
 434  , fst zoxuliProject ]
 435
 436renderSeries :: Series -> Zoxuli
 437renderSeries series = do
 438  h2_ series.name
 439  ol_ . mapM_ (\p -> li_ $ link False p p.title)
 440    $ series.pages
 441
 442seriesNav :: Page -> Maybe Zoxuli
 443seriesNav page =
 444  if null seriesWithPage then Nothing
 445  else Just $ mapM_ renderSeriesNav seriesWithPage
 446  where seriesWithPage = filter (elem page . (^. #pages)) allSeries
 447        renderSeriesNav :: Series -> Zoxuli
 448        renderSeriesNav (Series name ps) = span_ $ do
 449          let next xs x = lookup x . zip xs $ tail xs
 450              maybeLink t = maybe (pure ()) $ \p -> link False p t
 451              position = (+ 1) . fromMaybe 1 $ elemIndex page ps
 452          b_ "series"; " : "
 453          toHtml . show $ position; "/"
 454          toHtml . show $ length ps
 455          " in "
 456          a_ [ href_ $ "/series.html#" <> slugify name ]
 457            . toHtml $ name
 458          " : "
 459          maybeLink "[previous]" $ next (reverse ps) page
 460          " "
 461          maybeLink "[next]" $ next ps page
 462
 463-- | project pages
 464
 465projectWrapper :: Project -> Project
 466projectWrapper project =
 467  project & _1 % #html %~ (meta <>)
 468  where meta = do
 469          p_ [class_ "description"] .
 470            toHtml $ project ^. _2 % #description
 471          toc
 472          ul_ $ do
 473            li_ $ a_ [ href_ $ project ^. _2 % #repo ] $
 474              b_ "📦 SourceHut page"
 475            maybe (pure ()) li_ . seriesNav $ fst project
 476            li_ $ do
 477              b_ "tags"; " : "
 478              renderTags $ project ^. _1 % #tags
 479
 480projectPreview :: Project -> Zoxuli
 481projectPreview project = do
 482  b_ $ link False (fst project) $ project ^. _1 % #title
 483  " - "; toHtml $ project ^. _2 % #description
 484
 485projectPages :: [Project]
 486projectPages = projectWrapper <$> projectPages'
 487
 488projectPages' :: [Project]
 489projectPages' =
 490  [ zoxuliProject
 491  , swirlmaniaProject
 492  , coaltonRaylibProject
 493  ]
 494
 495zoxuliProject :: Project
 496zoxuliProject = makeProject "zoxuli"
 497  "a lucid static site generator"
 498  "https://git.sr.ht/~nebulae/zoxuli"
 499  ["web dev", "haskell", "programming"] $ do
 500  h2_ "a humble goal"
 501  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 ."
 502  p_ "this would lead me on an unexpectedly long journey that ends at the website you're on today ."
 503  h2_ "static site generators"
 504  p_ $ do
 505    "i started off by learning about "
 506    a_ [href_ "https://wordpress.com/"] "WordPress"
 507    " , 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 ."
 508  p_ $ do
 509    "looking for other options , i discovered "
 510    a_ [href_ "https://en.wikipedia.org/wiki/Static_site_generator"] "static site generators"
 511    " ( 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 "
 512    a_ [href_ "https://jekyllrb.com/"] "Jekyll"
 513    " , but the documentation was too advanced for me at the time . i moved on to other projects for a while ."
 514  h2_ "a new problem"
 515  p_ "a while later , after i had learned more about programming for other reasons , i realized i could now understand the Jekyll documentation !"
 516  p_ $ do "except ... now my expectations for my website had changed significantly . now i wanted :"
 517  ul_ $ do
 518    li_ "excellent accessibility"
 519    li_ "post series with navigation"
 520    li_ "tables of contents"
 521    li_ "tags that work across multiple categories of pages"
 522    li_ "backlinks"
 523    li_ "footnotes"
 524    li_ "breadcrumbs"
 525    li_ "control over where & how the aforementioned elements appear"
 526    li_ "syntax highlighting"
 527    li_ "LaTeX"
 528    li_ "semantic markup"
 529    li_ "no JavaScript"
 530    li_ "broken link checking"
 531    li_ "simple & powerful extensibility in a comfortable language"
 532    li_ "the ability to write pages in any markup format"
 533    li_ "a fast development feedback loop"
 534  p_ $ do
 535    "i thought , hey , that's a lot of stuff to ask for , but there are "
 536    a_ [href_ "https://jamstack.org/generators/"] "hundreds"
 537    " ( eor likely thousands ) of SSGs , so surely at least one of them can do all that ?"
 538  h2_ "SSG frenzy"
 539  p_ $ do
 540    "i tried a lot of SSGs . the three that got me closest to all of my goals were "
 541    a_ [href_ "https://soupault.app/"] "Soupault"; " , "
 542    a_ [href_ "https://bagatto.co/"] "Bagatto"; " , & "
 543    a_ [href_ "https://jaspervdj.be/hakyll/"] "Hakyll"
 544    " . but none of those got me everything ( "
 545    todo "explain their strengths & shortcomings in detail"
 546    " ) . there might be another one out there , but i didn't find it ."
 547  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 ."
 548  h2_ "zoxuli : first attempt" 
 549  p_ $ do
 550    "my first attempt ( "
 551    todo "share zoxuli v1"
 552    " ) was written in Common Lisp , & was most similar to Bagatto ."
 553  p_ $ do
 554    "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 "
 555    inlineCode ".("; " & "; inlineCode ")."
 556    " that would be evaluated when the site was built ."
 557  p_ $ do
 558    "this meant that - as an example - if one wanted to generate backlinks , one could create a "
 559    inlineCode ".(link <args>)."
 560    " function that would be used anywhere they needed links in their markup . the function would both render the link HTML ( using "
 561    a_ [href_ "https://github.com/ruricolist/spinneret"] "spinneret"
 562    " ) that gets inserted back into that position by zoxuli , & also store the link data into a variable . during the second pass , calls to a "
 563    inlineCode ".(backlinks)."
 564    " function would convert the collected links into backlinks & render them into HTML wherever they are needed ."
 565  h2_ "zoxuli : final version"
 566  p_ "after giving version one a proper go , these were my issues with it :"
 567  ul_ $ do
 568    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" 
 569    li_ "my implementation resulted in slower build times than i had hoped"
 570    li_ $ do
 571      "around that time , i realized i don't like programming in Lisp nearly as much as Haskell , especially after learning "
 572      a_ [href_ "https://www.parsonsmatt.org/2018/05/19/ghcid_for_the_win.html"] "how to use ghcid"
 573      " properly"
 574    li_ $ do
 575      "i also discovered the "
 576      a_ [href_ "https://hackage.haskell.org/package/lucid"] "lucid"
 577      " library for Haskell that i favored over spinneret for a few reasons"
 578  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 ."
 579  p_ $ do
 580    "i ported this website from Hakyll to zoxuli & i'm quite pleased with the experience . the source code for this website is on the "
 581    link True sourcePage "source page"
 582    " . "
 583
 584swirlmaniaProject :: Project
 585swirlmaniaProject = makeProject "swirlmania"
 586  "a StepMania level processor"
 587  "https://git.sr.ht/~nebulae/swirlmania"
 588  ["stepmania", "exercise", "rust"] $ do
 589  h2_ "what it is"
 590  p_ $ do
 591    "swirlmania is a CLI "
 592    a_ [href_ "https://www.stepmania.com/"] "StepMania"
 593    " simfile ( .ssc ) processor with support for generating tech charts without double steps ."
 594  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 ."
 595  h2_ "why i made it"
 596  p_ $ do
 597    "i enjoy playing "
 598    a_ [href_ "https://projectoutfox.com/"] "Project OutFox"
 599    " ( 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 ."
 600  p_ $ do
 601    "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 "
 602    a_ [href_ "https://github.com/phr00t/AutoStepper"] "AutoStepper"
 603    " , 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 ."
 604
 605coaltonRaylibProject :: Project
 606coaltonRaylibProject = makeProject "coalton-raylib"
 607  "games programming in Coalton"
 608  "https://git.sr.ht/~nebulae/coalton-raylib"
 609  ["game dev", "coalton", "common lisp", "programming"] $ do
 610  h2_ "the problem"
 611  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 ."
 612  p_ $ do
 613    "this is because one must restart the entire program they're working on every time a new change is compiled"
 614    footnote "there are ways to smooth this over , but they can add a lot of complexity"
 615    " ."
 616  h2_ "one possible solution"
 617  p_ $ do
 618    "as an ML language built on top of Common Lisp , "
 619    a_ [href_ "https://coalton-lang.github.io/20211010-introducing-coalton/"] "Coalton"
 620    " can provide a statically typed , functional ( with typeclasses ! ) , & highly interactive game programming environment ."
 621  p_ $ do
 622    "coalton-raylib is an example project i've shared that uses the "
 623    a_ [href_ "https://www.raylib.com/"] "raylib"
 624    " graphics library ."
 625  p_ $ do
 626    "i've written the draw loop in Common Lisp , & the data model & update functions in Coalton"
 627    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"
 628    " . 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 !"
 629  h2_ "conclusion"
 630  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 ."
 631
 632-- | post pages
 633
 634postWrapper :: Post -> Post
 635postWrapper post =
 636  post & _1 % #html %~ (meta <>)
 637  where meta = do
 638          toc
 639          ul_ $ do
 640            li_ $ b_ $ toHtml $ post ^. _2 % #date
 641            maybe (pure ()) li_ . seriesNav $ fst post
 642            li_ $ do
 643              b_ "tags"; " : "
 644              renderTags $ post ^. _1 % #tags
 645
 646postPreview :: Post -> Zoxuli
 647postPreview post = do
 648  link False (fst post) $ post ^. _1 % #title
 649
 650postPages :: [Post]
 651postPages = reverseChronological $ postWrapper <$> postPages'
 652
 653postPages' :: [Post]
 654postPages' =
 655  [ theOasisPost
 656  , timelineSplittingPost
 657  , compulsorySchoolingPost
 658  , howIUseComputersPost
 659  , unlicensePost
 660  , veganTipsPost
 661  , queerCybersecPost
 662  , inferiPost
 663  , wallOfShardsPost ]
 664
 665theOasisPost :: Post
 666theOasisPost = makePost "the oasis" "2024-11-25"
 667  ["virtual reality", "security", "game dev", "programming", "inclusive design", "formal verification"] $ do
 668  h2_ "introduction"
 669  p_ $ do
 670    "for a long time , i've been thinking about how to build a kinder & more radical version of the "
 671    a_ [href_ "https://readyplayerone.fandom.com/wiki/OASIS"] "OASIS"
 672    " from "
 673    a_ [href_ "https://en.wikipedia.org/wiki/Ready_Player_One"] "Ready Player One"
 674    " 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 ."
 675  h2_ "motivation"
 676  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 :"
 677  ul_ $ do
 678    li_ $ do
 679      "unlimited "
 680      a_ [href_ "#agency-over-avatar-&-environment"] "creative expression"
 681    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Morphological_freedom"] "morphological freedom"
 682    li_ "realistic interaction between people any distance apart"
 683    li_ $ do
 684      a_ [href_ "#formal-verification"] "formally verified"
 685      " "; a_ [href_ "#abuse-protection"] "abuse protection"
 686  h2_ "primary values"
 687  h3_ "agency over avatar & environment"
 688  blockquote_ [cite_ "https://applied-langua.ge/posts/here-is-a-loud-announcement.html#orga51f75c"] $ do
 689    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"
 690    p_ $ do
 691      " — Applied Language , "
 692      cite_ $ a_ [href_ "https://applied-langua.ge/posts/here-is-a-loud-announcement.html#orga51f75c"] "Maximalist computing"
 693  ul_ $ do
 694    li_ $ do
 695      "programmable , like "
 696      a_ [href_ "https://resonite.com/"] "Resonite"
 697      " is using "
 698      a_ [href_ "https://wiki.resonite.com/ProtoFlux"] "ProtoFlux"
 699      " ; the freedom to modify systems . see "
 700      a_ [href_ "https://youtu.be/eS8KeCOJBFA?si=WiUEa41EypVRfGvP"] "Rustybot's video on programming a flying device"
 701      " for a demonstration"
 702    li_ $ do
 703      "introspection to the core ; the ability to inspect any property of the oasis from inside it . this facilitates "
 704      a_ [href_ "https://plato.stanford.edu/entries/agency/"] "agency"
 705      " , if the fulfillment of one's agency's "
 706      a_ [href_ "https://plato.stanford.edu/entries/action/#ConsAim"] "constitutive aim"
 707      " is contingent on having accurate models of the world"
 708    li_ $ do "no "; a_ [href_ "https://www.deceptive.design/types"] "deceptive design patterns"; " ior advertisements"
 709    li_ $ do "no "; a_ [href_ "https://en.wikipedia.org/wiki/Artificial_scarcity"] "artificial scarcity"
 710    ul_ $ do
 711      li_ $ do link True (fst unlicensePost) "public domain"; " source code"
 712      li_ "anticapitalist ( unlike the OASIS )"
 713      li_ "no leveling hierarchy that locks features for new people"
 714      li_ $ do "no "; a_ [href_ "https://www.defectivebydesign.org/what_is_drm"] "DRM"
 715      li_ $ do "no "; a_ [href_ "https://youtu.be/YQ_xWvX1n9g?si=G0hjss5DYLXjdKPe"] "NFTs"; " ior cryptocurrencies"
 716      li_ "no limits on changing account metadata like usernames , with the exception of a UUID for persistent blocklists"
 717  h3_ "inclusive design"
 718  blockquote_ [cite_ "https://100daysofa11y.com/2019/12/03/accommodation-versus-inclusive-design/"] $ do
 719    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"
 720    p_ $ do
 721      " — Amy Carney , "
 722      cite_ $ a_ [href_ "https://100daysofa11y.com/2019/12/03/accommodation-versus-inclusive-design/"] "Accommodation versus Inclusive Design"
 723  ul_ $ do
 724    li_ $ do
 725      "create a core protocol like "
 726      a_ [href_ "https://en.wikipedia.org/wiki/Secure_Scuttlebutt"] "SSB"
 727      " eor "
 728      a_ [href_ "https://en.wikipedia.org/wiki/ActivityPub"] "ActivityPub"
 729      " to allow for multiple apps built upon it . "
 730      a_ [href_ "https://en.wikipedia.org/wiki/Inclusive_design"] "inclusive design"
 731      " 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" 
 732    li_ $ do
 733      a_ [href_ "https://iep.utm.edu/qualia/"] "qualia"
 734      "-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 "
 735      a_ [href_ "https://www.ifwiki.org/Parser-based_interactive_fiction"] "interactive fiction"
 736      " , ior more specifically , as a "
 737      a_ [href_ "https://en.wikipedia.org/wiki/Multi-user_dungeon"] "MUD"
 738      " . pure text can be accessed over a sweeping range of input & output devices , such as "
 739      a_ [href_ "https://en.wikipedia.org/wiki/Refreshable_braille_display"] "refreshable braille displays"
 740      " . here are few examples of what creation & programming could look like within interactive fiction :"
 741    ul_ $ do
 742      li_ $ a_ [href_ "https://netjam.org/quoth/"] "Quoth"
 743      li_ $ a_ [href_ "https://github.com/seisatsu/DennisMUD"] "DennisMUD"
 744      li_ $ a_ [href_ "https://wiki.xxiivv.com/site/paradise.html"] "Paradise"
 745      li_ $ a_ [href_ "https://git.sr.ht/~nebulae/nova_conjure"] "NovaConjure"
 746    li_ $ do
 747      "support many programming paradigms to accommodate varied neurotypes & prevent a "
 748      a_ [href_ "https://permacomputing.net/monoculture/"] "monoculture"
 749      " . the base language should make the creation of new languages ( spanning the range of each "
 750      a_ [href_ "https://madhadron.com/programming/seven_ur_languages.html"] "ur-language"
 751      " , & beyond ) simpler , as "
 752      a_ [href_ "https://beautifulracket.com/appendix/domain-specific-languages.html"] "Racket"
 753      " ior "
 754      a_ [href_ "https://wiki.haskell.org/Embedded_domain_specific_language"] "Haskell"
 755      " do"
 756    ul_ $ do
 757      li_ $ do
 758        "this could allow esoteric languages with radically different approaches like "
 759        a_ [href_ "https://www.dangermouse.net/esoteric/piet/samples.html"] "Piet"
 760        " to flourish"
 761      li_ $ do
 762        link True (fst coaltonRaylibProject) "Coalton"
 763        " & "
 764        a_ [href_ "https://hazel.org/"] "Hazel"
 765        " show that "
 766        a_ [href_ "https://en.wikipedia.org/wiki/ML_(programming_language)"] "ML"
 767        "-style programming can integrate into a highly dynamic simulation"
 768    li_ "thorough documentation for people with any degree of computing knowledge"
 769    li_ $ do
 770      "provide an accessibility toolkit so people can create new assistive devices within the oasis more easily , & handle "
 771      a_ [href_ "https://en.wikipedia.org/wiki/Internationalization_and_localization"] "i18n & l10n"
 772    li_ "avoid anthropocentric assumptions to future-proof the possible inclusion of non-human animals , AI agents , aliens , etc."
 773    li_ $ do
 774      "allow "
 775      a_ [href_ "https://pluralpedia.org/w/Plurality"] "plural"
 776      " systems to easily switch between profiles to match who is fronting , along with tools for communication between headmates"
 777    li_ $ do
 778      "runs natively on Linux , "
 779      a_ [href_ "https://en.wikipedia.org/wiki/Berkeley_Software_Distribution"] "BSD"
 780      " , & others"
 781    li_ $ do
 782      "see the "
 783      a_ [href_ "https://gameaccessibilityguidelines.com/full-list/"] "Game Accessibility Guidelines"
 784      " for more ideas in this realm"
 785  h3_ "abuse protection"
 786  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 ."
 787  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 ."
 788  ul_ $ do
 789    li_ $ do
 790      "use fine-grained "
 791      a_ [href_ "https://crochet.qteati.me/docs/reference/system/security/why.html"] "capability security"
 792      " for "
 793      a_ [href_ "https://medium.com/@robotlolita/dependencies-are-bad-but-necessary-806f38f65df4"] "safer dependencies"
 794      " while programming in the oasis ( like "
 795      a_ [href_ "https://crochet.qteati.me/docs/reference/system/whats-crochet.html"] "Crochet"
 796      " 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"
 797    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"
 798    li_ $ do
 799      "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 "
 800      link True (fst timelineSplittingPost) "timeline splitting block"
 801      " for more on this topic"
 802    li_ $ do
 803      "distributed/decentralized to counter emergent hierarchy & co-optation by corporations ior the state . the protocol should be ( non-blockchain ) "
 804      a_ [href_ "https://en.wikipedia.org/wiki/Peer-to-peer"] "P2P"
 805      " by default , with optional "
 806      a_ [href_ "https://en.wikipedia.org/wiki/Federation_(information_technology)"] "federation"
 807      " for specific instances where it's useful"
 808    li_ $ do
 809      "forums , git hosting , & any other external infrastructure should avoid regime-compliant software like Discord & GitHub if possible , & have a moderation policy that is "
 810      a_ [href_ "https://raddle.me/wiki/defining_our_terms"] "intolerant of bigotry"
 811    li_ $ do
 812      "multi-factor authentication , including "
 813      a_ [href_ "https://en.wikipedia.org/wiki/Universal_2nd_Factor"] "U2F"
 814    li_ $ do
 815      "accessible over "
 816      a_ [href_ "https://www.torproject.org/"] "TOR"
 817      " & "
 818      a_ [href_ "https://geti2p.net/en/"] "I2P"
 819    li_ $ do
 820      a_ [href_ "https://en.wikipedia.org/wiki/Homomorphic_encryption#Fully_homomorphic_encryption"] "fully homomorphic encryption"
 821      " could allow people to rely on the computational resources of others without necessarily trusting them"
 822    li_ $ do
 823      "compatible with "
 824      a_ [href_ "https://permacomputing.net/salvage_computing/"] "salvage computing"
 825      " . if the oasis is to be built upon new hardware , it must wait until the current system of "
 826      a_ [href_ "https://youtu.be/vmtaWQxBYFk?si=UotkxTCkwUbPqgQ4"] "exploitative electronics production"
 827      " is destroyed , & a kinder means of production has taken its place"
 828  h3_ "formal verification"
 829  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 ."
 830  ul_ $ do
 831    li_ $ do
 832      "deductive verification using "
 833      a_ [href_ "https://en.wikipedia.org/wiki/Dependent_type"] "dependently typed"
 834      " programming ( a "
 835      a_ [href_ "https://en.wikipedia.org/wiki/Formal_verification"] "formal verification"
 836      " approach ) could ensure , using mathematical proofs , that entire sets of bugs cannot occur . this would be used in conjunction with "
 837      a_ [href_ "https://en.wikipedia.org/wiki/Secure_by_design"] "secure design"
 838      " to prevent exploits in addition to bugs"
 839    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 :"
 840    ul_ $ do
 841      li_ "improve the learning materials . it is uncertain how much this would help , but i am hopeful over a very long time frame"
 842      li_ $ do
 843        "use "
 844        a_ [href_ "https://en.wikipedia.org/wiki/Refinement_type"] "refinement types"
 845        " where possible . "
 846        a_ [href_ "https://ucsd-progsys.github.io/liquidhaskell/"] "LiquidHaskell"
 847        " could be combined with "
 848        a_ [href_ "https://wiki.portal.chalmers.se/agda/pmwiki.php"] "Agda"
 849        " using "
 850        a_ [href_ "https://agda.github.io/agda2hs/"] "agda2hs"
 851        " , for instance"
 852      li_ $ do
 853        a_ [href_ "https://github.com/magmide/magmide"] "Magmide"
 854        " 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"
 855    li_ $ do
 856      "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 "
 857      a_ [href_ "https://medium.com/@maiavictor/towards-a-simple-theorem-prover-5005a1e66a6f"] "Cedille"
 858      " , eor "
 859      a_ [href_ "https://whatisrt.github.io/meta-cedille/2019/06/25/syntactic-metaprogramming-i.html"] "Meta-cedille"
 860      " if syntactic metaprogramming were helpful"
 861  h2_ "further reading"
 862  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 ) :"
 863  ul_ $ do
 864    li_ $ a_ [href_ "https://en.wikibooks.org/wiki/The_World_of_Peer-to-Peer_(P2P)"] "The World of Peer-to-Peer"
 865    li_ $ a_ [href_ "https://ssbc.github.io/scuttlebutt-protocol-guide/"] "the Scuttlebutt Protocol Guide"
 866    li_ $ a_ [href_ "https://softwarefoundations.cis.upenn.edu/"] "Software Foundations"
 867    li_ $ a_ [href_ "https://plfa.github.io/"] "Programming Language Foundations in Agda"
 868    li_ $ a_ [href_ "https://beautifulracket.com/"] "Beautiful Racket"
 869    li_ $ a_ [href_ "https://crochet.qteati.me/docs/reference/system/index.html"] "The Crochet System"
 870    li_ $ a_ [href_ "https://fuchsia.dev/fuchsia-src/concepts/principles/secure"] "Fuchsia OS security docs"
 871  h2_ "conclusion"
 872  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 ."
 873  p_ $ do
 874    "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 "
 875    link True contactPage "contact me"
 876    " !"
 877
 878timelineSplittingPost :: Post
 879timelineSplittingPost = makePost "timeline splitting block" "2024-11-16"
 880  ["game dev", "security", "queer"] $ do
 881  h2_ "safety features aren't strong enough"
 882  p_ $ do
 883    "when i've visited social spaces in "
 884    a_ [href_ "https://en.wikipedia.org/wiki/Virtual_reality"] "virtual reality"
 885    " , 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 :"
 886  ul_ $ do
 887    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"
 888    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"
 889    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"
 890    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"
 891    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"
 892    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."
 893    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"
 894    li_ "reports i've made haven't resulted in much action , & if they do , it usually takes at least a week"
 895  h3_ "trivial fixes"
 896  p_ "potential patches for some of the above issues :"
 897  ul_ $ do
 898    li_ "don't make social consensus necessary to completely remove someone from your environment"
 899    li_ "allow hiding & muting your surroundings while using the menu"
 900    li_ "ensure it's easy to see the username of anyone you're interacting with"
 901    li_ "add an option to share who you've blocked with others , along with your reasons for each block"
 902    li_ "allow blocking discreetly & in bulk"
 903    li_ "add a shortcut so you can block faster"
 904  h2_ "a more potent kind of block"
 905  p_ $ do "my experiences have motivated me to think of an entirely different way to implement blocking ."
 906  p_ $ do
 907    "suppose you're in "
 908    link True (fst theOasisPost) "the oasis"
 909    " , & 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 ."
 910    p_ "should the message be visible to you ?"
 911  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 ."
 912  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"; " ."
 913  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 ."
 914  h3_ "timeline divergence"
 915  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 ."
 916  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 ."
 917  p_ $ do
 918    "sophisticated tools for merging timelines à la "
 919    a_ [href_ "https://git-scm.com/docs/git-merge"] "git"
 920    " could prevent people from losing changes when switching timelines ."
 921  h3_ "choosing timelines"
 922  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 ."
 923
 924compulsorySchoolingPost :: Post
 925compulsorySchoolingPost = makePost "compulsory schooling is abusive" "2024-10-11"
 926  ["youth liberation", "philosophy"] $ do
 927  h2_ "even if ..."
 928  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 ."
 929  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 ."
 930  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 ."
 931  h2_ "the problem"
 932  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 ."
 933  p_ $ do
 934    "if someone did the same thing to "; b_ "all"; " adults"
 935    footnote "not entirely hypothetical ; wage slavery approaches a weaker form of this"
 936    " , & 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 ."
 937  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 ."
 938  h2_ "how to fix it"
 939  p_ "my take is fairly straightforward :"
 940  ul_ $ do
 941    li_ "make educational facilities & resources entirely opt-in"
 942    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 !"
 943    li_ "actually give children agency over the design of those resources , & give them full control over what they get to learn about"
 944    li_ "stop conditioning adult staff to act like fucking cops"
 945    li_ "be extremely cautious about creating pressure on children to visit any given educational environment"
 946  p_ $ do
 947    "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 \""
 948    a_ [href_ "https://en.wikipedia.org/wiki/Third_place"] "third places"
 949    "\" than are currently available ."
 950  hr_ []
 951  h2_ "an aside on argumentation for youth liberation"
 952  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 ."
 953  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 ."
 954  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 ."
 955  p_ $ do
 956    "but what they blatantly omit from their arguments is that compulsory schooling is a problem "
 957    em_ "because it's abusive to assert ownership over another person , steal their agency , & gaslight them about their personhood"
 958    " ."
 959  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 ."
 960
 961queerCybersecPost :: Post
 962queerCybersecPost = makePost "queer cybersecurity resources" "2024-07-07"
 963  ["security", "privacy", "queer", "guide"] $ do
 964  h2_ "introduction"
 965  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 !"
 966  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 ."
 967  h2_ "privacy & security"
 968  "these links are rather general in purpose , so you may need to explore them a bit to find what you are looking for :"
 969  ul_ $ do
 970    li_ $ a_ [href_ "https://gendersec.tacticaltech.org/wiki/index.php/Complete_manual"] "Zen & the art of making tech work for you - Gender & Tech Resources"
 971    li_ $ a_ [href_ "https://ssd.eff.org/"] "Surveillance Self-Defense - Electronic Frontier Foundation"
 972    li_ $ a_ [href_ "https://www.privacyguides.org/en/"] "Privacy Guides"
 973  h2_ "doxing"
 974  p_ "these guides go over restorative & preventative strategies for people affected by doxing :"
 975  ul_ $ do
 976    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"
 977    li_ $ a_ [href_ "https://gendersec.tacticaltech.org/wiki/index.php/Self-dox"] "Self-dox - Gender & Tech Resources"
 978    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"
 979  h2_ "social media"
 980  p_ $ do
 981    "i'd strongly advise looking into safer alternatives ( such as well moderated instances on the "
 982    a_ [href_ "https://www.fediverse.to/"] "fediverse"
 983    " ) , 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 :"
 984  p_ $ a_ [href_ "https://righttobe.org/guides/how-to-use-social-media-safely/"] "How To Use Social Media Safely - Right To Be"
 985
 986inferiPost :: Post
 987inferiPost = makePost "inferi monologue" "2024-05-20" ["fiction"] $ do
 988  p_ $ do
 989    "a headcannon of how "
 990    a_ [href_ "https://pottermore.fandom.com/wiki/Inferi"] "inferi"
 991    " would greet travelers :"
 992  blockquote_ $ do
 993    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 ."
 994    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 ."
 995    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"; " ."
 996    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 ."
 997    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 ."
 998    p_ "stop struggling , [REDACTED] ."
 999    p_ $ em_ "we already told you , the waters are cozy ."
1000
1001howIUseComputersPost :: Post
1002howIUseComputersPost = makePost "how i use computers" "2024-08-24"
1003  ["qubesOS", "programming", "haskell", "purescript"] $ do
1004  h2_ "introduction"
1005  p_ $ do
1006    "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 "
1007    link True (fst theOasisPost) "my ideal computing environment"
1008    " ."
1009  todo "explain choices in more detail"
1010  h2_ "hardware"
1011  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 ."
1012  p_ $ do
1013    "my primary computer is a Thinkpad T430 running "
1014    a_ [href_ "https://www.qubes-os.org/"] "QubesOS"
1015    " . i chose this laptop because , out of the options that can be flashed with "
1016    a_ [href_ "https://www.coreboot.org/"] "coreboot"
1017    " , it was old enough to be somewhat affordable"
1018    footnote "around 200 USD on Newegg"
1019    " , while still running fast enough for most tasks that don't involve graphics ( & some that do ) ."
1020  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 ."
1021  p_ $ do
1022    "my \"smart\"phone is a Pixel 4a"
1023    footnote "this was also around 200 USD on eBay"
1024    " running CalyxOS"
1025    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 ."
1026    " ."
1027  h2_ "software"
1028  p_ $ do
1029    "i primarily use CalyxOS for taking notes with "
1030    a_ [href_ "https://f-droid.org/packages/com.orgzly/"] "Orgzly"
1031    " , tracking sleep with "
1032    a_ [href_ "https://f-droid.org/packages/hu.vmiklos.plees_tracker/"] "Plees Tracker"
1033    " , web browsing away from home , insecure communications , & listening to audio-based educational materials ."
1034  p_ "i use QubesOS for web browsing , programming , Serious Writing , & other forms of expression ."
1035  p_ $ do
1036    "i chose QubesOS because i find "
1037    a_ [href_ "https://en.wikipedia.org/wiki/Ambient_authority"] "ambient authority"
1038    " horrifying , but i'm not aware of any robust "
1039    a_ [href_ "https://en.wikipedia.org/wiki/Capability-based_security"] "capability-based"
1040    " operating systems that would fill the same need for me ."
1041  p_ $ do
1042    "QubesOS is slow & "
1043    a_ [href_ "https://github.com/QubesOS/qubes-issues/issues/5907"] "doesn't have screen reader support"
1044    " yet , but it does have some great features like configuration using "
1045    a_ [href_ "https://www.qubes-os.org/doc/salt/"] "Salt"
1046    footnote $ do
1047      "ior maybe even Nix-like configuration in the future if "
1048      a_ [href_ "https://spectrum-os.org/"] "Spectrum"
1049      " goes well"
1050    ", Whonix qubes , & offline qubes . here are a few things i've learned that make it more tolerable :"
1051  ul_ $ do
1052    li_ $ do
1053      "install "
1054      a_ [href_ "https://i3wm.org/"] "i3"
1055    ul_ $ do
1056      li_ $ do
1057        "name qubes using two-character strings so they can be searched for more quickly using "
1058        a_ [href_ "https://wiki.archlinux.org/title/Dmenu"] "dmenu"
1059      li_ "make shortcuts for locking the screen & toggling the status bar"
1060      li_ $ do "make a shortcut ( mine is bound to "; kbd_ "Alt"; " + ";kbd_ "i"; " ) for opening a dmenu list of helper scripts & frequently opened programs"
1061    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"
1062    li_ $ do
1063      "dark mode can be enabled in "
1064      a_ [href_ "https://xfce.org/"] "XFCE"
1065      " qubes by "
1066      inlineCode "qvm-run"
1067      "ing the following command in each of them :"
1068      code "bash" "xfconf-query -c xsettings -p /Net/ThemeName -s 'Adwaita-dark'"
1069    li_ $ do
1070      "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 "
1071      a_ [href_ "https://www.borgbackup.org/"] "BorgBackup"
1072    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"
1073    li_ $ do
1074      a_ [href_ "https://wiki.archlinux.org/title/Rofi"] "rofi"
1075      " can be opened inside a qube using "
1076      inlineCode "qvm-run"
1077      " in dom0 scripts , but the rofi flag "
1078      inlineCode "-normal-window"
1079      " must be added to disable "
1080      inlineCode "override_redirect"
1081      " , which allows the program window to appear undecorated & on top in "
1082      a_ [href_ "https://en.wikipedia.org/wiki/X_Window_System"] "X11"
1083  h2_ "programming"
1084  h3_ "language"
1085  p_ $ do
1086    "currently , the languages i find most useful are "
1087    a_ [href_ "https://www.haskell.org/"] "Haskell"
1088    " & "
1089    a_ [href_ "https://www.purescript.org/"] "PureScript"
1090    " ( which is very similar to Haskell , but compiles to JavaScript ) ."
1091  p_ "a few things i like about them are :"
1092  ul_ $ do
1093    li_ "very clean syntax"
1094    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Pure_function"] "functional purity"
1095    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system"] "Hindley-Milner type system"
1096    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Type_class"] "typeclasses"
1097    li_ $ do
1098      a_ [href_ "https://hoogle.haskell.org/"] "Hoogle"
1099      "/"
1100      a_ [href_ "https://pursuit.purescript.org/"] "Pursuit"
1101    li_ $ do
1102      a_ [href_ "https://github.com/ndmitchell/ghcid"] "ghcid"
1103      "/"
1104      a_ [href_ "https://github.com/kRITZCREEK/pscid"] "pscid"
1105    li_ "high abstraction power"
1106    li_ $ do
1107      "automatically "
1108      a_ [href_ "https://en.wikipedia.org/wiki/Currying"] "curried"
1109      " functions"
1110    li_ $ a_ [href_ "https://en.wikipedia.org/wiki/Lazy_evaluation"] "lazy evaluation"
1111    li_ $ do
1112      a_ [href_ "https://en.wikipedia.org/wiki/Row_polymorphism"] "row polymorphism"
1113      " ( PureScript only )"
1114    li_ $ do
1115      "wonderful libraries like "
1116      a_ [href_ "https://github.com/ajnsit/concur"] "Concur"
1117      " , "
1118      a_ [href_ "https://hackage.haskell.org/package/optics"] "optics"
1119      " & "
1120      a_ [href_ "https://hackage.haskell.org/package/apecs"] "apecs"
1121  p_ $ do
1122    "i'm autistic & get overwhelmed by extraneous detail "; em_ "very"; " easily , so i view many of these as accessibility features"
1123    footnote "however , i have heard that for some people , these languages are unpleasant to work with for the some of the same reasons ."
1124    " , 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 ."
1125  p_ $ do
1126    "for native programs that target embedded devices ior need to be "
1127    a_ [href_ "https://en.wikipedia.org/wiki/Static_build"] "statically built"
1128    " ior "
1129    a_ [href_ "https://en.wikipedia.org/wiki/Cross_compiler"] "cross compiled"
1130    " , "
1131    a_ [href_ "https://www.rust-lang.org/"] "Rust"
1132    " is a nice language that provides a somewhat similar experience to Haskell , while making those tasks much easier ."
1133  p_ $ do
1134    "i've experimented a lot with "
1135    a_ [href_ "https://nixos.org/"] "Nix"
1136    " 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"
1137    footnote $ do
1138      "i've seen many folks point out that the "
1139      a_ [href_ "https://discourse.nixos.org/"] "Nix forum"
1140      " & 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 "
1141      a_ [href_ "https://en.wikipedia.org/wiki/Anduril_Industries"] "Anduril"
1142      " ."
1143    " . i've definitely benefited from Nix before , but those benefits have only barely outweighed the many negatives i've experienced along the way . maybe "
1144    a_ [href_ "https://lix.systems/"] "Lix"
1145    " & "
1146    a_ [href_ "https://auxolotl.org/en/"] "Aux"
1147    " will manage to solve some of those issues ."
1148  h3_ "code editor"
1149  p_ $ do
1150    "the code editor i use most often is "
1151    a_ [href_ "https://helix-editor.com/"] "helix"; " ."
1152  p_ $ do
1153    "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"
1154    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 ."
1155    " . once i was proficient with vim , i moved on to "
1156    a_ [href_ "https://github.com/doomemacs/doomemacs"] "Doom Emacs"
1157    " ."
1158  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 ."
1159  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 ." 
1160
1161unlicensePost :: Post
1162unlicensePost = makePost "why i use the Unlicense & CC0" "2024-07-21"
1163  ["anticopyright", "philosophy"] $ do
1164  h2_ "introduction"
1165  p_ $ do
1166    "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"
1167    footnote "it's also not a comprehensive argument , as there are quite a few reasons i dislike \"intellectual property\" that aren't mentioned here ."
1168    " ."
1169  h2_ "\"intellectual property\" relies on death"
1170  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\" ."
1171  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 ."
1172  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 ?"
1173  h2_ "copyleft & copyfarleft"
1174  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 ."
1175  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 ?"
1176  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 ."
1177  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 ."
1178  h2_ "even copyleft causes psychic damage"
1179  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 ."
1180  p_ $ do
1181    "anecdotally , trying to make sense of the rules for using copyleft software"
1182    footnote "which is an instance of trying to navigate the legal system"
1183    " 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 ."
1184  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 ."
1185  h2_ "releasing work into the public domain"
1186  p_ $ do
1187    "regarding anticopyright praxis , my current preference is to use the "
1188    a_ [href_ "https://unlicense.org/"] "Unlicense"
1189    " for code , & "
1190    a_ [href_ "https://creativecommons.org/public-domain/cc0/"] "CC0"
1191    " for everything else ."
1192  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\" ."
1193
1194veganTipsPost :: Post
1195veganTipsPost = makePost "a few vegan tips" "2024-07-03"
1196  ["vegan", "guide"] $ do
1197  h2_ "introduction"
1198  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 ."
1199  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 ."
1200  h2_ "the tips"
1201  ul_ $ do
1202    li_ "be careful with stickers & sticky things . bugs can become trapped on them easily"
1203    li_ $ do "be "; em_ "very"; " distrustful of unfamiliar non-vegans’ assessments of whether things are vegan eor not"
1204    li_ $ do
1205      "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"
1206      footnote "i almost never see bugs trapped in the tub anymore after doing this"
1207    li_ $ do
1208      "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 . "
1209      todo "link to a detailed guide"
1210    li_ "don’t assume that random household items like toilet paper are vegan by default"
1211    li_ $ do
1212      "fireworks are "
1213      a_ [href_ "https://www.animal-ethics.org/how-fireworks-harm-nonhuman-animals/"] "harmful ior deadly"
1214      " to many living things"
1215    li_ "avoid unnecessary loud noises that could distress nearby animals . some music may be an exception , though , if it's not too loud"
1216    li_ "don’t walk when the ground is unlit to avoid crushing bugs"
1217    li_ $ do "reduce ior eliminate reliance on car travel for "; em_ "many"; " reasons"
1218    li_ "recreational fires often burn insects alive & wood smoke can be harmful to animals"
1219    li_ "avoid planting poisonous plants where wild animals might eat them"
1220  h2_ "related writing"
1221  p_ $ do
1222    "if one's interested in more practical advice of this nature , i can recommend the writings of a researcher named "
1223    a_ [href_ "https://briantomasik.com/"] "Brian Tomasik"
1224    " . 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 ."
1225
1226wallOfShardsPost :: Post
1227wallOfShardsPost = makePost "wall of shards" "2024-05-10"
1228  ["fiction"] $ do
1229  h2_ "loading world ..."
1230  p_ $ do
1231    "type "; inlineCode "start"
1232    " to collect a shard ."
1233  code "haskell" "start"
1234  p_ $ em_ "shard count : 2"
1235  h2_ "your lab"
1236  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 ."
1237  code "haskell" "talkTo b6e"
1238  p_ "you break the silence ."
1239  blockquote_ "are you ready to get started ?"
1240  blockquote_ "yeah . what will it feel like , exactly ? like will i even notice you're there ?"
1241  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 ."
1242  blockquote_ "too bad i can't take a nap during it ."
1243  blockquote_ "i know right . well , if you're ready then have a seat & i'll start projecting ."
1244  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 ."
1245  code "haskell" "use Terminal"
1246  p_ $ do
1247    "you swivel over to your terminal & execute "
1248    inlineCode "1ntr0j3ct.hs"; " ."
1249  blockquote_ "you named the script using leetspeak ?"
1250  blockquote_ "yep , it makes words taste better ."
1251  p_ "you get up & lie down on your yoga mat . your vision starts to fade ..."
1252  h2_ "the center"
1253  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 ."
1254  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 ?"
1255  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 ."
1256  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 ."
1257  code "haskell" "go West"
1258  p_ "there is a cliff face to the west . you hover up to the top edge ."
1259  h2_ "eastern plateau ridge"
1260  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 ?"
1261  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 ."
1262  p_ "there's a rectangular slot in the ground close by , but you can't see what's in it from here ."
1263  code "haskell" "examine Slot"
1264  h2_ "slot machine"
1265  p_ "now that you're closer to the slot , you notice a hand-written sign next to it that says :"
1266  blockquote_ "slot machine : enter the slot & slide like a coin to a random location"
1267  p_ "somebody walks over to you & asks what you're doing ."
1268  blockquote_ "hello ! i'm trying to figure out what the deal with this slot thing is ."
1269  blockquote_ "no you're not . i saw you over there trying to vandalize my hand-written sign ."
1270  blockquote_ "you what ? i didn't even touch the sign ."
1271  blockquote_ "then how did all of those purple stains get on it ?"
1272  p_ "you glance at the sign again . it does appear more stained than you remember it . you wonder , aloud :"
1273  blockquote_ "maybe it's from that pool over there ? whatever gravity well is swirling above the pool might have become volatile ."
1274  blockquote_ "that's certainly a possiblity . but it seems much more likely that you hated my handwriting style & wanted to destroy it yourself ."
1275  blockquote_ "you serious ? i didn't even think of the handwriting style until now. it was perfectly serviceable ."
1276  blockquote_ "oh ."
1277  p_ "they gaze into the distance uncomfortably ."
1278  p_ "the grinding sound grows louder , & the crimson glow brighter ."
1279  p_ $ em_ "shard count : 3"
1280  code "haskell" "ask SignPainter . about Sound"
1281  p_ "you ask the sign painter if they know where that sound is coming from ."
1282  blockquote_ "no ."
1283  code "haskell" "ask SignPainter . about CrimsonGlow"
1284  p_ "you ask them if they know what the crimson glow is coming from ."
1285  blockquote_ "no ."
1286  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 ."
1287  p_ "it starts to rain . the droplets are purple , & stains your grey dress ."
1288  code "haskell" "go East"
1289  p_ "you hover back down the cliff face & land on the boardwalk again ."
1290  h2_ "the center"
1291  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 ."
1292  code "haskell" "go West"
1293  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 ."
1294  p_ "but no . that would be a gamble ."
1295  p_ "you don't like leaving things to chance ."
1296  code "haskell" "go North"
1297  p_ "with your mind made up , you hover northward along the boardwalk ."
1298  h2_ "an expanse"
1299  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 ."
1300  p_ "there is a swarm of winged , metal centipedes flying over your head ."
1301  p_ "the grinding sound is loud enough here to put you on edge ."
1302  p_ $ em_ "shard count : 4"
1303  code "haskell" "ask Centipede . about Sound"
1304  p_ "you call out to the centipedes above you ."
1305  blockquote_ "excuse me , would any of you be able to help me answer a quick question ?"
1306  p_ "one of them lands in front of you . they are wearing a really cool sweater with a fractal pattern knitted onto it ."
1307  blockquote_ "i might be able to help ya ! what's ur question ?"
1308  blockquote_ "oh nice ! thank you . do you know what that glass grinding noise in the distance is ?"
1309  blockquote_ "ya mean death ?"
1310  blockquote_ "what ?"
1311  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 !"
1312  p_ "the other centipedes start gliding to the southeast ."
1313  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 ?"
1314  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 ."
1315  blockquote_ "ok , well thanks for the info . i'll keep searching then ."
1316  blockquote_ "be careful ."
1317  p_ "they fly away ."
1318  code "haskell" "save"
1319  p_ $ em_ "serializing timeline ..."
1320  p_ $ em_ "game saved ."
1321  p_ $ em_ $ do
1322    "enter the code "
1323    inlineCode "5nJ0kL3-001"
1324    " to restore the game to this point in time ."
1325  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 *"