Compare commits

..

1 Commits

Author SHA1 Message Date
James Eversole
3172ca2f59 Add markdown response support 2026-04-23 10:53:56 -05:00
7 changed files with 159 additions and 53 deletions

35
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": { "nodes": {
"haskell-flake": { "haskell-flake": {
"locked": { "locked": {
"lastModified": 1728845985, "lastModified": 1776783293,
"narHash": "sha256-0KkAWCRBNpno3f+E1rvV9TOr0iuweqncWGn1KtbrGmo=", "narHash": "sha256-Na95Y2awqZsLhFNfBNbLj0hk4zyE3eKUROB2o9Qdqi8=",
"owner": "srid", "owner": "srid",
"repo": "haskell-flake", "repo": "haskell-flake",
"rev": "2393b55948866f39afcfa7d8a53893a096bcd284", "rev": "f52ac89b2232dd50e5d1110416ebc5bbb09265bd",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -17,30 +17,33 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1701282334, "lastModified": 1764521362,
"narHash": "sha256-MxCVrXY6v4QmfTwIysjjaX0XUhqBbxTWWB4HXtDYsdk=", "narHash": "sha256-M101xMtWdF1eSD0xhiR8nG8CXRlHmv6V+VoY65Smwf4=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "057f9aecfb71c4437d2b27d3323df7f93c010b7e", "rev": "871b9fd269ff6246794583ce4ee1031e1da71895",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nixos", "owner": "nixos",
"ref": "23.11", "ref": "25.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs-lib": { "nixpkgs-lib": {
"locked": { "locked": {
"lastModified": 1727825735, "lastModified": 1774748309,
"narHash": "sha256-0xHYkMkeLVQAMa7gvkddbPqpxph+hDzdu1XdGPJR+Os=", "narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=",
"type": "tarball", "owner": "nix-community",
"url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" "repo": "nixpkgs.lib",
"rev": "333c4e0545a6da976206c74db8773a1645b5870a",
"type": "github"
}, },
"original": { "original": {
"type": "tarball", "owner": "nix-community",
"url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz" "repo": "nixpkgs.lib",
"type": "github"
} }
}, },
"parts": { "parts": {
@@ -48,11 +51,11 @@
"nixpkgs-lib": "nixpkgs-lib" "nixpkgs-lib": "nixpkgs-lib"
}, },
"locked": { "locked": {
"lastModified": 1727826117, "lastModified": 1775087534,
"narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=", "narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
"owner": "hercules-ci", "owner": "hercules-ci",
"repo": "flake-parts", "repo": "flake-parts",
"rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1", "rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -1,6 +1,6 @@
{ {
inputs = { inputs = {
nixpkgs.url = "github:nixos/nixpkgs/23.11"; nixpkgs.url = "github:nixos/nixpkgs/25.11";
haskell-flake.url = "github:srid/haskell-flake"; haskell-flake.url = "github:srid/haskell-flake";
parts.url = "github:hercules-ci/flake-parts"; parts.url = "github:hercules-ci/flake-parts";
}; };
@@ -17,7 +17,7 @@
haskellProjects.default = { haskellProjects.default = {
basePackages = pkgs.haskellPackages; basePackages = pkgs.haskellPackages;
packages = { packages = {
http2.source = "3.0.3"; http2.source = "5.3.10";
}; };
devShell = { devShell = {
enable = true; enable = true;

View File

@@ -15,23 +15,23 @@ executable sampu
default-extensions: OverloadedStrings default-extensions: OverloadedStrings
ghc-options: -threaded -rtsopts -with-rtsopts=-N -optl-pthread -fPIC ghc-options: -threaded -rtsopts -with-rtsopts=-N -optl-pthread -fPIC
build-depends: base build-depends: base
, bytestring >= 0.11.5.0 , bytestring
, clay >= 0.14.0 , clay
, commonmark >= 0.2.4 , commonmark
, directory >= 1.3.7.0 , directory
, dotenv >= 0.11.0.0 , dotenv
, feed >= 1.3.2.0 , feed
, filemanip >= 0.3.6.1 , filemanip
, filepath >= 1.4.2.2 , filepath
, http-types , http-types
, lucid >= 2.11.0 , lucid
, text >= 2.0 , text
, time >= 1.12.0 , time
, twain >= 2.1.0.0 , twain
, wai-extra >= 3.0 && < 3.2 , wai-extra
, wai-middleware-static >= 0.9.0 , wai-middleware-static
, warp == 3.3.25 , warp
, xml-conduit >= 1.9.1.0 , xml-conduit
hs-source-dirs: src hs-source-dirs: src
other-modules: Core.Configuration other-modules: Core.Configuration
Core.Feed Core.Feed

75
scratch/tricu.cabal Normal file
View File

@@ -0,0 +1,75 @@
cabal-version: 1.12
name: tricu
version: 0.19.0
description: A micro-language for exploring Tree Calculus
author: James Eversole
maintainer: james@eversole.co
copyright: James Eversole
license: ISC
license-file: LICENSE
build-type: Simple
extra-source-files:
README.md
executable tricu
main-is: Main.hs
hs-source-dirs:
src
default-extensions:
DeriveDataTypeable
LambdaCase
MultiWayIf
OverloadedStrings
ghc-options: -threaded -rtsopts -with-rtsopts=-N -optl-pthread -fPIC
build-depends:
base >=4.7
, cmdargs
, containers
, exceptions
, filepath
, haskeline
, megaparsec
, mtl
, text
, transformers
other-modules:
Eval
FileEval
Lexer
Parser
REPL
Research
default-language: Haskell2010
test-suite tricu-tests
type: exitcode-stdio-1.0
main-is: Spec.hs
hs-source-dirs: test, src
default-extensions:
DeriveDataTypeable
LambdaCase
MultiWayIf
OverloadedStrings
build-depends:
base
, cmdargs
, containers
, exceptions
, filepath
, haskeline
, megaparsec
, mtl
, tasty
, tasty-hunit
, tasty-quickcheck
, text
, transformers
default-language: Haskell2010
other-modules:
Eval
FileEval
Lexer
Parser
REPL
Research

View File

@@ -5,9 +5,8 @@ import Data.Text
import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy as LT
import qualified Text.Atom.Feed as Atom import qualified Text.Atom.Feed as Atom
import qualified Text.Feed.Export as Export (textFeedWith) import qualified Text.Feed.Export as Export (textFeed)
import Text.Feed.Types import Text.Feed.Types
import Text.XML (def, rsPretty)
data Post = Post { _date :: Text data Post = Post { _date :: Text
, _url :: Text , _url :: Text
@@ -21,7 +20,7 @@ autoFeed baseFeed feedData = baseFeed { Atom.feedEntries = fmap toEntry feedData
-- Render the Atom Feed to Lazy Text -- Render the Atom Feed to Lazy Text
renderFeed :: Atom.Feed -> LT.Text renderFeed :: Atom.Feed -> LT.Text
renderFeed = fromJust . Export.textFeedWith def{rsPretty = True} . AtomFeed renderFeed = fromJust . Export.textFeed . AtomFeed
-- Convert a Post to an Atom Entry -- Convert a Post to an Atom Entry
toEntry :: Post -> Atom.Entry toEntry :: Post -> Atom.Entry

View File

@@ -10,6 +10,7 @@ import qualified Text.Atom.Feed as Atom
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lazy (ByteString)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.Encoding as TLE import qualified Data.Text.Lazy.Encoding as TLE
import Data.Time.Clock (UTCTime(..), getCurrentTime) import Data.Time.Clock (UTCTime(..), getCurrentTime)
import Data.Time.Format (formatTime, defaultTimeLocale) import Data.Time.Format (formatTime, defaultTimeLocale)
@@ -24,6 +25,10 @@ import Web.Twain hiding (fileName)
-- A ResponoderM capable of lifting to IO monad; constructs response to clients -- A ResponoderM capable of lifting to IO monad; constructs response to clients
index :: ResponderM a index :: ResponderM a
index = do index = do
markdownRequested <- acceptsMarkdown
if markdownRequested
then sendMarkdownFile "./data/posts/home.md"
else do
(title, homeMd, footerMd) <- liftIO $ (,,) (title, homeMd, footerMd) <- liftIO $ (,,)
<$> Conf.title <$> Conf.title
<*> mdFileToLucid "./data/posts/home.md" <*> mdFileToLucid "./data/posts/home.md"
@@ -39,6 +44,10 @@ posts = do
case postValid of case postValid of
False -> missing False -> missing
True -> do True -> do
markdownRequested <- acceptsMarkdown
if markdownRequested
then sendMarkdownFile $ postPath postName
else do
(title, footerMd, postMd) <- liftIO $ (,,) (title, footerMd, postMd) <- liftIO $ (,,)
<$> Conf.title <$> Conf.title
<*> mdFileToLucid "./data/posts/footer.md" <*> mdFileToLucid "./data/posts/footer.md"
@@ -104,6 +113,10 @@ feed = do
-- Refer to index comments -- Refer to index comments
contact :: ResponderM a contact :: ResponderM a
contact = do contact = do
markdownRequested <- acceptsMarkdown
if markdownRequested
then sendMarkdownFile "./data/posts/contact.md"
else do
(title, contactMd, footerMd) <- liftIO $ (,,) (title, contactMd, footerMd) <- liftIO $ (,,)
<$> Conf.title <$> Conf.title
<*> mdFileToLucid "./data/posts/contact.md" <*> mdFileToLucid "./data/posts/contact.md"
@@ -114,6 +127,19 @@ contact = do
theme :: ResponderM a theme :: ResponderM a
theme = send $ css $ TLE.encodeUtf8 $ S.cssRender S.composedStyles theme = send $ css $ TLE.encodeUtf8 $ S.cssRender S.composedStyles
acceptsMarkdown :: ResponderM Bool
acceptsMarkdown = do
acceptHeader <- header "Accept"
return $ maybe False (T.isInfixOf "text/markdown" . T.toLower) acceptHeader
sendMarkdownFile :: FilePath -> ResponderM a
sendMarkdownFile path = do
markdown <- liftIO $ mdFileToMarkdown path
send $ withHeader (hContentType, "text/markdown; charset=utf-8")
$ raw status200 []
$ TLE.encodeUtf8
$ LT.fromStrict markdown
-- Helper function for responding in ResponderM from Html -- Helper function for responding in ResponderM from Html
sendLucidFragment :: Html () -> ResponderM a sendLucidFragment :: Html () -> ResponderM a
sendLucidFragment x = send $ html $ lucidToTwain x sendLucidFragment x = send $ html $ lucidToTwain x

View File

@@ -20,6 +20,9 @@ mdToLucid cmtextinput = case (commonmark "" cmtextinput) of
mdFileToLucid :: FilePath -> IO (LU.Html ()) mdFileToLucid :: FilePath -> IO (LU.Html ())
mdFileToLucid path = fmap (mdToLucid . decodeUtf8) (B.readFile path) mdFileToLucid path = fmap (mdToLucid . decodeUtf8) (B.readFile path)
mdFileToMarkdown :: FilePath -> IO Text
mdFileToMarkdown path = fmap decodeUtf8 (B.readFile path)
mdFileToText :: FilePath -> IO (Text) mdFileToText :: FilePath -> IO (Text)
mdFileToText path = do mdFileToText path = do
htmlContent <- mdFileToLucid path htmlContent <- mdFileToLucid path