web-rep 0.7.2 → 0.8.0
raw patch · 19 files changed
+298/−638 lines, 19 filesdep +bifunctorsdep −doctestdep −network-simpledep −numhaskdep ~attoparsecdep ~boxdep ~box-socketsetup-changednew-component:exe:web-rep-example
Dependencies added: bifunctors
Dependencies removed: doctest, network-simple, numhask, tasty, tasty-hspec
Dependency ranges changed: attoparsec, box, box-socket, clay, concurrency, generic-lens, interpolatedstring-perl6, lens, lucid, mtl, text, transformers, unordered-containers, wai-middleware-static, wai-websockets, websockets
Files
- ChangeLog.md +4/−0
- Setup.hs +1/−0
- app/rep-example.hs +5/−7
- readme.md +0/−27
- src/Web/Rep.hs +1/−3
- src/Web/Rep/Bootstrap.hs +5/−3
- src/Web/Rep/Examples.hs +37/−47
- src/Web/Rep/Html.hs +7/−2
- src/Web/Rep/Html/Input.hs +15/−15
- src/Web/Rep/Mathjax.hs +0/−156
- src/Web/Rep/Page.hs +35/−40
- src/Web/Rep/Render.hs +6/−4
- src/Web/Rep/Server.hs +7/−7
- src/Web/Rep/Shared.hs +24/−26
- src/Web/Rep/SharedReps.hs +37/−41
- src/Web/Rep/Socket.hs +57/−48
- stack.yaml +0/−17
- test/test.hs +0/−92
- web-rep.cabal +57/−103
+ ChangeLog.md view
@@ -0,0 +1,4 @@+0.8.0+===++* Removed numhask dependencies
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
app/rep-example.hs view
@@ -3,16 +3,15 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -Wall #-} -import NumHask.Prelude import Options.Generic import Web.Rep import Web.Rep.Examples+import Prelude data AppType = SharedTest deriving (Eq, Read, Show, Generic) @@ -22,10 +21,9 @@ instance ParseFields AppType -data Opts w- = Opts- { apptype :: w ::: AppType <?> "type of example"- }+newtype Opts w = Opts+ { apptype :: w ::: AppType <?> "type of example"+ } deriving (Generic) instance ParseRecord (Opts Wrapped)@@ -34,4 +32,4 @@ main = do o :: Opts Unwrapped <- unwrapRecord "examples for web-page" case apptype o of- SharedTest -> defaultSharedServer (maybeRep (Just "maybe") False repExamples)+ SharedTest -> defaultSharedServer (maybeRep (Just "maybe") False repExamples)
− readme.md
@@ -1,27 +0,0 @@-[web-rep](https://github.com/tonyday567/web-rep) [](https://travis-ci.org/tonyday567/web-rep)-===--Various functions and representations for a web page.--The best way to understand functionality is via running the example app:--```-stack build --test --exec "$(stack path --local-install-root)/bin/page-example --apptype SharedTest" --file-watch-```--http://localhost:9160/--reference------- [scotty](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/flags.html#flag-reference)-- get [bootstrap](https://getbootstrap.com/)-- [bootstrap-slider](https://seiyria.com/bootstrap-slider)-- [blaze](http://hackage.haskell.org/package/blaze-html)-- [lucid](http://hackage.haskell.org/package/lucid)-- [clay](https://www.stackage.org/clay)--todo------https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Do_not_ever_use_eval!
src/Web/Rep.hs view
@@ -51,7 +51,6 @@ module Web.Rep.Html, module Web.Rep.Html.Input, module Web.Rep.Bootstrap,- module Web.Rep.Mathjax, -- * re-exports HashMap.HashMap,@@ -60,12 +59,11 @@ import qualified Data.HashMap.Strict as HashMap import Web.Rep.Bootstrap-import Web.Rep.Socket import Web.Rep.Html import Web.Rep.Html.Input-import Web.Rep.Mathjax import Web.Rep.Page import Web.Rep.Render import Web.Rep.Server import Web.Rep.Shared import Web.Rep.SharedReps+import Web.Rep.Socket
src/Web/Rep/Bootstrap.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -Wall #-} -- | Some <https://getbootstrap.com/ bootstrap> assets and functionality.@@ -17,9 +16,12 @@ ) where +import Control.Monad.State.Lazy+import Data.Bool+import Data.Functor.Identity+import Data.Text (Text) import Lucid import Lucid.Base-import NumHask.Prelude import Web.Rep.Html import Web.Rep.Page import Web.Rep.Shared
src/Web/Rep/Examples.hs view
@@ -4,13 +4,12 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -Wall #-} module Web.Rep.Examples ( page1, page2,- pagemj, cfg2, RepExamples (..), repExamples,@@ -30,48 +29,40 @@ import qualified Clay import Control.Lens hiding ((.=)) import Data.Attoparsec.Text+import Data.Biapplicative+import Data.Bool+import Data.Text (Text, pack)+import GHC.Generics import Lucid-import NumHask.Prelude import Text.InterpolatedString.Perl6 import Web.Rep -- | simple page example page1 :: Page page1 =- #htmlBody .~ button1- $ #cssBody .~ RepCss css1- $ #jsGlobal .~ mempty- $ #jsOnLoad .~ click- $ #libsCss .~ (libCss <$> cssLibs)- $ #libsJs .~ (libJs <$> jsLibs)- $ mempty+ #htmlBody .~ button1 $+ #cssBody .~ RepCss css1 $+ #jsGlobal .~ mempty $+ #jsOnLoad .~ click $+ #libsCss .~ (libCss <$> cssLibs) $+ #libsJs .~ (libJs <$> jsLibs) $+ mempty -- | page with localised libraries page2 :: Page page2 =- #libsCss .~ (libCss <$> cssLibsLocal)- $ #libsJs .~ (libJs <$> jsLibsLocal)- $ page1---- | simple mathjax formulae-pagemj :: Page-pagemj = mathjaxPage & #htmlBody .~ htmlMathjaxExample--htmlMathjaxExample :: HtmlT Identity ()-htmlMathjaxExample =- p_ "double dollar:"- <> p_ "$$\\sum_{i=0}^n i^2 = \\frac{(n^2+n)(2n+1)}{6}$$"- <> p_ "single dollar for inline: $\\sum_{i=0}^n i^2 = \\frac{(n^2+n)(2n+1)}{6}$"- <> p_ "escaped brackets for inline mathjax: \\(\\sum_{i=0}^n i^2 = \\frac{(n^2+n)(2n+1)}{6}\\)"+ #libsCss .~ (libCss <$> cssLibsLocal) $+ #libsJs .~ (libJs <$> jsLibsLocal) $+ page1 cfg2 :: PageConfig cfg2 =- #concerns .~ Separated- $ #pageRender .~ Pretty- $ #structure .~ Headless- $ #localdirs .~ ["test/static"]- $ #filenames .~ (("other/cfg2" <>) <$> suffixes)- $ defaultPageConfig ""+ #concerns .~ Separated $+ #pageRender .~ Pretty $+ #structure .~ Headless $+ #localdirs .~ ["test/static"] $+ #filenames .~ (("other/cfg2" <>) <$> suffixes) $+ defaultPageConfig "" cssLibs :: [Text] cssLibs =@@ -114,19 +105,18 @@ ("Go " <> with i_ [class__ "fa fa-play"] mempty) -- | One of each sharedrep input instances.-data RepExamples- = RepExamples- { repTextbox :: Text,- repTextarea :: Text,- repSliderI :: Int,- repSlider :: Double,- repCheckbox :: Bool,- repToggle :: Bool,- repDropdown :: Int,- repDropdownMultiple :: [Int],- repShape :: Shape,- repColor :: Text- }+data RepExamples = RepExamples+ { repTextbox :: Text,+ repTextarea :: Text,+ repSliderI :: Int,+ repSlider :: Double,+ repCheckbox :: Bool,+ repToggle :: Bool,+ repDropdown :: Int,+ repDropdownMultiple :: [Int],+ repShape :: Shape,+ repColor :: Text+ } deriving (Show, Eq, Generic) -- | For a typed dropdown example.@@ -237,10 +227,10 @@ _ -> SumOnly defInt = case defst of SumInt i -> i- _ -> defi+ _NotSumInt -> defi defText = case defst of SumText t -> t- _ -> deft+ _NotSumText -> deft data SumType2Example = SumOutside Int | SumInside SumTypeExample deriving (Eq, Show, Generic) @@ -287,7 +277,7 @@ case repst2' of "SumOutside" -> SumOutside repoi' "SumInside" -> SumInside repst'- _ -> SumOutside repoi'+ _WeirdSpelling -> SumOutside repoi' defInt = case defst of SumInt i -> i- _ -> defi+ _NotSumInt -> defi
src/Web/Rep/Html.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE IncoherentInstances #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StrictData #-}+{-# OPTIONS_GHC -Wall #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -- | Key generators and miscellaneous html utilities.@@ -17,10 +18,14 @@ ) where +import Data.Bool import Data.List (intersperse)+import Data.Text (Text, pack) import qualified Data.Text.Lazy as Lazy import Lucid-import NumHask.Prelude++-- $setup+-- >>> :set -XOverloadedStrings -- | FIXME: A horrible hack to separate class id's class__ :: Text -> Attribute
src/Web/Rep/Html/Input.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -Wall #-} -- | Common web page input elements, often with bootstrap scaffolding.@@ -11,24 +10,25 @@ ) where -import Data.Text (split)+import Data.Bool+import Data.Maybe+import Data.Text (Text, pack, split)+import GHC.Generics import Lucid import Lucid.Base-import NumHask.Prelude hiding (for_) import Web.Rep.Html -- | something that might exist on a web page and be a front-end input to computations.-data Input a- = Input- { -- | underlying value- inputVal :: a,- -- | label suggestion- inputLabel :: Maybe Text,- -- | name//key//id of the Input- inputId :: Text,- -- | type of html input- inputType :: InputType- }+data Input a = Input+ { -- | underlying value+ inputVal :: a,+ -- | label suggestion+ inputLabel :: Maybe Text,+ -- | name//key//id of the Input+ inputId :: Text,+ -- | type of html input+ inputType :: InputType+ } deriving (Eq, Show, Generic) -- | Various types of web page inputs, encapsulating practical bootstrap class functionality
− src/Web/Rep/Mathjax.hs
@@ -1,156 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# OPTIONS_GHC -Wall #-}---- | mathjax functionality.------ some <https://docs.mathjax.org/en/latest/web/start.html mathjax> assets------ <https://math.meta.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference mathjax cheatsheet>------ FIXME: Mathjax inside svg doesn't quite work, and needs to be structured around the foreign object construct.-module Web.Rep.Mathjax- ( mathjaxPage,- mathjax27Page,- mathjaxSvgPage,- mathjax27SvgPage,- )-where--import Control.Lens-import Lucid-import NumHask.Prelude-import Text.InterpolatedString.Perl6-import Web.Rep.Page--mathjax3Lib :: Html ()-mathjax3Lib =- with- (script_ mempty)- [ src_ "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-svg.js",- id_ "MathJax-script",- async_ ""- ]--mathjax27Lib :: Html ()-mathjax27Lib =- with- (script_ mempty)- [ src_ "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_SVG"- ]--jqueryLib :: Html ()-jqueryLib =- with- (script_ mempty)- [ src_ "https://code.jquery.com/jquery-3.3.1.slim.min.js",- integrity_ "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo",- crossorigin_ "anonymous"- ]---- | A 'Page' that loads mathjax-mathjaxPage :: Page-mathjaxPage =- mempty- & #jsGlobal .~ RepJsText scriptMathjaxConfig- & #libsJs- .~ [ mathjax3Lib- ]---- | A 'Page' that loads the mathjax 2.7 js library-mathjax27Page :: Page-mathjax27Page =- mempty- & #jsOnLoad .~ RepJsText scriptMathjaxConfig- & #libsJs- .~ [ mathjax27Lib- ]---- | A 'Page' that tries to enable mathjax inside svg (which is tricky).-mathjaxSvgPage :: Text -> Page-mathjaxSvgPage cl =- mempty- & #jsGlobal .~ RepJsText (scriptMathjaxConfigSvg cl)- & #libsJs- .~ [ mathjax3Lib,- jqueryLib- ]---- | A 'Page' that tries to enable mathjax 2.7 inside svg.-mathjax27SvgPage :: Text -> Page-mathjax27SvgPage cl =- mempty- & #jsGlobal .~ RepJsText (scriptMathjax27ConfigSvg cl)- & #libsJs- .~ [ mathjax27Lib,- jqueryLib- ]--scriptMathjaxConfig :: Text-scriptMathjaxConfig =- [q|-MathJax = {- tex: {- inlineMath: [ ['$','$'], ["\\(","\\)"] ]- }-};-|]---- | Mathjax applies within an svg element as normal, but results in an svg element inside a text element which is not allowed, hence the extra scripting.--- http://bl.ocks.org/larsenmtl/86077bddc91c3de8d3db6a53216b2f47-scriptMathjax27ConfigSvg :: Text -> Text-scriptMathjax27ConfigSvg cl =- [qq|- setTimeout(() => \{-- MathJax.Hub.Config(\{- tex2jax: \{- inlineMath: [ ['$','$'], ["\\(","\\)"] ],- processEscapes: true- }- });-- MathJax.Hub.Register.StartupHook("End", function() \{- setTimeout(() => \{- $('.{cl}').each(function()\{- var m = $('text>span>svg');- m.remove();- $(this).append(m);- });-- }, 1);- });- }, 1);-|]---- | Mathjax applies within an svg element as normal, but results in an svg element inside a text element which is not allowed, hence the extra scripting.--- http://bl.ocks.org/larsenmtl/86077bddc91c3de8d3db6a53216b2f47-scriptMathjaxConfigSvg :: Text -> Text-scriptMathjaxConfigSvg cl =- [qq|- window.MathJax = \{- tex: \{- inlineMath: [ ['$','$'], ["\\(","\\)"] ]- },- startup: \{- ready: () => \{- MathJax.startup.defaultReady();- MathJax.startup.promise.then(() => \{- var xs = document.querySelectorAll('.{cl}');- xs.forEach((x) =>- \{ x.querySelectorAll('text').forEach((t) =>- \{t.querySelectorAll('mjx-container>svg').forEach((s) =>- \{- Array.from(t.attributes).forEach((a) => s.setAttribute(a.name, a.value));- x.appendChild(s);- });- });- });- });- }- }- };-|]
src/Web/Rep/Page.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE StrictData #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-} {-# OPTIONS_HADDOCK hide, not-home #-}@@ -20,13 +20,11 @@ PageConcerns (..), PageStructure (..), PageRender (..),- -- $css Css, RepCss (..), renderCss, renderRepCss,- -- $js JS (..), RepJs (..),@@ -34,41 +32,41 @@ renderRepJs, parseJs, renderJs,- )+ ) where -import qualified Clay import Clay (Css)+import qualified Clay import Control.Lens import Data.Generics.Labels ()-import GHC.Show (show)+import Data.Text (Text, unpack)+import Data.Text.Lazy (toStrict)+import GHC.Generics import Language.JavaScript.Parser import Language.JavaScript.Parser.AST import Language.JavaScript.Process.Minify import Lucid-import NumHask.Prelude hiding (show) import Text.InterpolatedString.Perl6 -- | Components of a web page. -- -- A web page can take many forms but still have the same underlying representation. For example, CSS can be linked to in a separate file, or can be inline within html, but still be the same css and have the same expected external effect. A Page represents the practical components of what makes up a static snapshot of a web page.-data Page- = Page- { -- | css library links- libsCss :: [Html ()],- -- | javascript library links- libsJs :: [Html ()],- -- | css- cssBody :: RepCss,- -- | javascript with global scope- jsGlobal :: RepJs,- -- | javascript included within the onLoad function- jsOnLoad :: RepJs,- -- | html within the header- htmlHeader :: Html (),- -- | body html- htmlBody :: Html ()- }+data Page = Page+ { -- | css library links+ libsCss :: [Html ()],+ -- | javascript library links+ libsJs :: [Html ()],+ -- | css+ cssBody :: RepCss,+ -- | javascript with global scope+ jsGlobal :: RepJs,+ -- | javascript included within the onLoad function+ jsOnLoad :: RepJs,+ -- | html within the header+ htmlHeader :: Html (),+ -- | body html+ htmlBody :: Html ()+ } deriving (Show, Generic) instance Semigroup Page where@@ -90,12 +88,11 @@ -- | A web page typically is composed of some css, javascript and html. -- -- 'Concerns' abstracts this structural feature of a web page.-data Concerns a- = Concerns- { cssConcern :: a,- jsConcern :: a,- htmlConcern :: a- }+data Concerns a = Concerns+ { cssConcern :: a,+ jsConcern :: a,+ htmlConcern :: a+ } deriving (Eq, Show, Foldable, Traversable, Generic) instance Functor Concerns where@@ -137,14 +134,13 @@ deriving (Show, Eq, Generic) -- | Configuration options when rendering a 'Page'.-data PageConfig- = PageConfig- { concerns :: PageConcerns,- structure :: PageStructure,- pageRender :: PageRender,- filenames :: Concerns FilePath,- localdirs :: [FilePath]- }+data PageConfig = PageConfig+ { concerns :: PageConcerns,+ structure :: PageStructure,+ pageRender :: PageRender,+ filenames :: Concerns FilePath,+ localdirs :: [FilePath]+ } deriving (Show, Eq, Generic) -- | Default configuration is inline ecma and css, separate html header and body, minified code, with the suggested filename prefix.@@ -186,7 +182,6 @@ -- | Render 'Css' as text. renderCss :: Css -> Text renderCss = toStrict . Clay.render- -- | wrapper for `JSAST` newtype JS = JS {unJS :: JSAST} deriving (Show, Eq, Generic)
src/Web/Rep/Render.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-} -- | Page rendering module Web.Rep.Render@@ -13,9 +12,12 @@ ) where +import Control.Applicative import Control.Lens+import Control.Monad+import Data.Foldable+import Data.Text (Text, pack, unpack) import Lucid-import NumHask.Prelude import Web.Rep.Html import Web.Rep.Page @@ -117,14 +119,14 @@ -- | Render Page concerns to files. renderPageToFile :: FilePath -> PageConfig -> Page -> IO () renderPageToFile dir pc page =- sequenceA_ $ liftA2 writeFile' (pc ^. #filenames) (renderPageAsText pc page)+ sequenceA_ $ liftA2 writeFile' (pc ^. #filenames) (fmap unpack (renderPageAsText pc page)) where writeFile' fp s = unless (s == mempty) (writeFile (dir <> "/" <> fp) s) -- | Render a page to just a Html file. renderPageHtmlToFile :: FilePath -> PageConfig -> Page -> IO () renderPageHtmlToFile file pc page =- writeFile file (toText $ renderPageHtmlWith pc page)+ writeFile file (unpack $ toText $ renderPageHtmlWith pc page) -- | Render a Page as Text. renderPageAsText :: PageConfig -> Page -> Concerns Text
src/Web/Rep/Server.hs view
@@ -1,21 +1,21 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-} {-# OPTIONS_GHC -Wall #-} -- | Serve pages via 'ScottyM' module Web.Rep.Server- ( servePageWith+ ( servePageWith, ) where import Control.Lens hiding (only)+import Control.Monad+import Control.Monad.Trans.Class+import Data.Text (unpack) import Lucid import Network.Wai.Middleware.Static (addBase, noDots, only, staticPolicy)-import NumHask.Prelude hiding (get, replace)-import Web.Rep.Render import Web.Rep.Page+import Web.Rep.Render import Web.Scotty -- | serve a Page via a ScottyM@@ -33,8 +33,8 @@ get rp ( do- lift $ writeFile' cssfp css- lift $ writeFile' jsfp js+ lift $ writeFile' cssfp (unpack css)+ lift $ writeFile' jsfp (unpack js) html $ renderText h ) cssfp = pc ^. #filenames . #cssConcern
@@ -1,14 +1,13 @@-{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE StrictData #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-} {-# OPTIONS_HADDOCK hide, not-home #-} module Web.Rep.Shared- (- RepF (..),+ ( RepF (..), Rep, oneRep, SharedRepF (..),@@ -22,23 +21,25 @@ ) where +import Control.Applicative import Control.Lens+import Control.Monad.IO.Class+import Control.Monad.State.Lazy+import Data.Biapplicative import Data.Generics.Labels ()-import qualified Data.HashMap.Strict as HashMap import Data.HashMap.Strict (HashMap)-import GHC.Show (show)+import qualified Data.HashMap.Strict as HashMap+import Data.Text (Text, pack) import Lucid-import NumHask.Prelude hiding (show) -- | -- Information contained in a web page can usually be considered to be isomorphic to a map of named values - a 'HashMap'. This is especially true when considering a differential of information contained in a web page. Looking at a page from the outside, it often looks like a streaming differential of a hashmap. -- -- RepF consists of an underlying value being represented, and, given a hashmap state, a way to produce a representation of the underlying value (or error), in another domain, together with the potential to alter the hashmap state.-data RepF r a- = Rep- { rep :: r,- make :: HashMap Text Text -> (HashMap Text Text, Either Text a)- }+data RepF r a = Rep+ { rep :: r,+ make :: HashMap Text Text -> (HashMap Text Text, Either Text a)+ } deriving (Functor) -- | the common usage, where the representation domain is Html@@ -94,11 +95,9 @@ -- This is sometimes referred to as "observable sharing". See <http://hackage.haskell.org/package/data-reify data-reify> as another library that reifies this (pun intended), and provided the initial inspiration for this implementation. -- -- unshare should only be run once, which is a terrible flaw that might be fixed by linear types.----newtype SharedRepF m r a- = SharedRep- { unshare :: StateT (Int, HashMap Text Text) m (RepF r a)- }+newtype SharedRepF m r a = SharedRep+ { unshare :: StateT (Int, HashMap Text Text) m (RepF r a)+ } deriving (Functor) -- | default representation type of 'Html' ()@@ -148,10 +147,10 @@ (f name a) ( \s -> ( s,- join- $ maybe (Left "lookup failed") Right- $ either (Left . (\x -> name <> ": " <> x)) Right . p <$>- HashMap.lookup name s+ join $+ maybe (Left "lookup failed") Right $+ either (Left . (\x -> name <> ": " <> x)) Right . p+ <$> HashMap.lookup name s ) ) @@ -175,10 +174,10 @@ (f name a) ( \s -> ( HashMap.delete name s,- join- $ maybe (Right $ Right d) Right- $ p <$>- HashMap.lookup name s+ join $+ maybe (Right $ Right d) Right $+ p+ <$> HashMap.lookup name s ) ) @@ -204,4 +203,3 @@ (Rep h fa, (_, m)) <- runSharedRep sr action h m pure (fa m)-
@@ -3,10 +3,9 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE StrictData #-} {-# LANGUAGE TupleSections #-}-{-# LANGUAGE NoImplicitPrelude #-} {-# OPTIONS_GHC -Wall #-}-{-# OPTIONS_GHC -Wredundant-constraints #-} -- | Various SharedRep instances for common html input elements. module Web.Rep.SharedReps@@ -42,20 +41,23 @@ import Box.Cont () import Control.Lens+import Control.Monad+import Control.Monad.State.Lazy import Data.Attoparsec.Text hiding (take)+import qualified Data.Attoparsec.Text as A+import Data.Biapplicative+import Data.Bool import qualified Data.HashMap.Strict as HashMap import qualified Data.List as List-import Data.Text (intercalate)+import Data.Text (Text, intercalate, pack, unpack) import Lucid-import NumHask.Prelude hiding (intercalate, takeWhile) import Text.InterpolatedString.Perl6 import Web.Rep.Bootstrap import Web.Rep.Html import Web.Rep.Html.Input-import Web.Rep.Shared import Web.Rep.Page-import qualified Prelude as P-import qualified Data.Attoparsec.Text as A+import Web.Rep.Shared+import Prelude as P -- $setup -- >>> :set -XOverloadedStrings@@ -72,8 +74,7 @@ -- | initial value a -> SharedRep m a-repInput p pr i a =- register (first pack . A.parseOnly p) pr (\n v -> toHtml $ #inputVal .~ v $ #inputId .~ n $ i) a+repInput p pr i = register (first pack . A.parseOnly p) pr (\n v -> toHtml $ #inputVal .~ v $ #inputId .~ n $ i) -- | Like 'repInput', but does not put a value into the HashMap on instantiation, consumes the value when found in the HashMap, and substitutes a default on lookup failure repMessage :: (Monad m, ToHtml a) => Parser a -> (a -> Text) -> Input a -> a -> a -> SharedRep m a@@ -84,7 +85,7 @@ -- -- For Example, a slider between 0 and 1 with a step of 0.01 and a default value of 0.3 is: ----- >>> :t slider (Just "label") 0 1 0.01 0.3+-- > :t slider (Just "label") 0 1 0.01 0.3 -- slider (Just "label") 0 1 0.01 0.3 :: Monad m => SharedRep m Double slider :: (Monad m) =>@@ -105,7 +106,7 @@ -- -- For Example, a slider between 0 and 1000 with a step of 10 and a default value of 300 is: ----- >>> :t sliderI (Just "label") 0 1000 10 300+-- > :t sliderI (Just "label") 0 1000 10 300 -- sliderI (Just "label") 0 1000 10 300 -- :: (Monad m, ToHtml a, P.Integral a, Show a) => SharedRep m a sliderI ::@@ -125,7 +126,7 @@ -- | textbox classique ----- >>> :t textbox (Just "label") "some text"+-- > :t textbox (Just "label") "some text" -- textbox (Just "label") "some text" :: Monad m => SharedRep m Text textbox :: (Monad m) => Maybe Text -> Text -> SharedRep m Text textbox label v =@@ -304,10 +305,10 @@ (toHtml (Input v label name (Checkbox v)) <> scriptToggleShow name id') ( \s -> ( s,- join- $ maybe (Left "HashMap.lookup failed") Right- $ either (Left . pack) Right . parseOnly ((== "true") <$> takeText)- <$> HashMap.lookup name s+ join $+ maybe (Left "HashMap.lookup failed") Right $+ either (Left . pack) Right . parseOnly ((== "true") <$> takeText)+ <$> HashMap.lookup name s ) ) @@ -327,12 +328,12 @@ accordionList :: (Monad m) => Maybe Text -> Text -> Maybe Text -> (Text -> a -> SharedRep m a) -> [Text] -> [a] -> SharedRep m [a] accordionList title prefix open srf labels as = SharedRep $ do (Rep h fa) <-- unshare- $ first (accordion prefix open . zip labels)- $ foldr- (\a x -> bimap (:) (:) a <<*>> x)- (pure [])- (zipWith srf labels as)+ unshare $+ first (accordion prefix open . zip labels) $+ foldr+ (\a x -> bimap (:) (:) a <<*>> x)+ (pure [])+ (zipWith srf labels as) h' <- zoom _1 h pure (Rep (maybe mempty (h5_ . toHtml) title <> h') fa) @@ -340,21 +341,16 @@ accordionBoolList :: (Monad m) => Maybe Text -> Text -> (a -> SharedRep m a) -> (Bool -> SharedRep m Bool) -> [Text] -> [(Bool, a)] -> SharedRep m [(Bool, a)] accordionBoolList title prefix bodyf checkf labels xs = SharedRep $ do (Rep h fa) <-- unshare- $ first (accordionChecked prefix)- $ first (zipWith (\l (ch, a) -> (l, a, ch)) labels)- $ foldr- (\a x -> bimap (:) (:) a <<*>> x)- (pure [])- ( ( \(ch, a) ->- bimap- (,)- (,)- (checkf ch)- <<*>> bodyf a- )- <$> xs+ unshare $+ first+ ( accordionChecked prefix+ . zipWith (\l (ch, a) -> (l, a, ch)) labels )+ ( foldr+ (\a x -> bimap (:) (:) a <<*>> x)+ (pure [])+ ((\(ch, a) -> bimap (,) (,) (checkf ch) <<*>> bodyf a) <$> xs)+ ) h' <- zoom _1 h pure (Rep (maybe mempty (h5_ . toHtml) title <> h') fa) @@ -403,7 +399,7 @@ parsed str = case reads str of [(a, "")] -> Right a- _ -> Left (pack str)+ _badRead -> Left (pack str) -- | Representation of web concerns (css, js & html). fiddle :: (Monad m) => Concerns Text -> SharedRep m (Concerns Text, Bool)@@ -449,20 +445,20 @@ hmap dd' cs' = div_ ( dd'- <> mconcat (zipWith (\c t -> subtype c t0 t) cs' ts)+ <> mconcat (zipWith (`subtype` t0) cs' ts) ) mmap dd' cs' = maybe (List.head cs') (cs' List.!!) (List.elemIndex dd' ts) -- | select test keys from a Map-selectItems :: [Text] -> HashMap.HashMap Text a -> [(Text,a)]+selectItems :: [Text] -> HashMap.HashMap Text a -> [(Text, a)] selectItems ks m = HashMap.toList $ HashMap.filterWithKey (\k _ -> k `elem` ks) m -- | rep of multiple items list repItemsSelect :: Monad m => [Text] -> [Text] -> SharedRep m [Text]-repItemsSelect init full =- dropdownMultiple (A.takeWhile (`notElem` ([',']::[Char]))) id (Just "items") full init+repItemsSelect initial full =+ dropdownMultiple (A.takeWhile (`notElem` ([','] :: [Char]))) id (Just "items") full initial subtype :: With a => a -> Text -> Text -> a subtype h origt t =
src/Web/Rep/Socket.hs view
@@ -3,8 +3,8 @@ {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE StrictData #-} {-# OPTIONS_GHC -Wall #-}-{-# OPTIONS_GHC -Wredundant-constraints #-} -- | A socket between a web page and haskell, based on the box library. module Web.Rep.Socket@@ -12,44 +12,49 @@ serveSocketBox, sharedServer, defaultSharedServer,- SocketConfig(..),+ SocketConfig (..), defaultSocketConfig, defaultSocketPage, defaultInputCode, defaultOutputCode,- Code(..),+ Code (..), code, wrangle, ) where -import qualified Network.WebSockets as WS import Box import Box.Socket import Control.Lens+import Control.Monad import Control.Monad.Conc.Class as C+import Control.Monad.State.Lazy+import qualified Data.Attoparsec.Text as A+import Data.Bifunctor import Data.HashMap.Strict as HashMap+import Data.Text (Text, pack) import qualified Data.Text as Text-import NumHask.Prelude hiding (intercalate, replace)+import GHC.Generics+import Lucid as L+import Network.Wai.Handler.WebSockets+import qualified Network.WebSockets as WS import Text.InterpolatedString.Perl6+import Web.Rep.Bootstrap import Web.Rep.Html import Web.Rep.Page import Web.Rep.Server import Web.Rep.Shared-import Web.Rep.Bootstrap import Web.Scotty hiding (get)-import qualified Data.Attoparsec.Text as A-import Network.Wai.Handler.WebSockets-import Lucid as L socketPage :: Page-socketPage = mempty & #jsOnLoad .~- mconcat- [ webSocket,- runScriptJs,- refreshJsbJs,- preventEnter- ]+socketPage =+ mempty & #jsOnLoad+ .~ mconcat+ [ webSocket,+ runScriptJs,+ refreshJsbJs,+ preventEnter+ ] serveSocketBox :: SocketConfig -> Page -> Box IO Text Text -> IO () serveSocketBox cfg p b =@@ -59,8 +64,8 @@ sharedServer :: SharedRep IO a -> SocketConfig -> Page -> (Html () -> [Code]) -> (Either Text a -> IO [Code]) -> IO () sharedServer srep cfg p i o =- serveSocketBox cfg p <$.>- fromAction (backendLoop srep i o . wrangle)+ serveSocketBox cfg p+ <$.> fromAction (backendLoop srep i o . wrangle) defaultSharedServer :: (Show a) => SharedRep IO a -> IO () defaultSharedServer srep =@@ -68,18 +73,18 @@ defaultSocketPage :: Page defaultSocketPage =- bootstrapPage <>- socketPage &- #htmlBody- .~ divClass_- "container"- ( mconcat- [ divClass_ "row" (h1_ "web-rep testing"),- divClass_ "row" $ mconcat $ (\(t, h) -> divClass_ "col" (h2_ (toHtml t) <> L.with div_ [id_ t] h)) <$> sections- ]- )+ bootstrapPage+ <> socketPage+ & #htmlBody+ .~ divClass_+ "container"+ ( mconcat+ [ divClass_ "row" (h1_ "web-rep testing"),+ divClass_ "row" $ mconcat $ (\(t, h) -> divClass_ "col" (h2_ (toHtml t) <> L.with div_ [id_ t] h)) <$> sections+ ]+ ) where- sections = + sections = [ ("input", mempty), ("output", mempty) ]@@ -92,7 +97,8 @@ (Html () -> [Code]) -> -- | output code (Either Text a -> m [Code]) ->- Box m [Code] (Text, Text) -> m ()+ Box m [Code] (Text, Text) ->+ m () backendLoop sr inputCode outputCode (Box c e) = flip evalStateT (0, HashMap.empty) $ do -- you only want to run unshare once for a SharedRep (Rep h fa) <- unshare sr@@ -108,14 +114,13 @@ b <- lift $ commit c o when b (go fa) updateS Nothing s = s- updateS (Just (k,v)) s = second (insert k v) s+ updateS (Just (k, v)) s = second (insert k v) s step' fa = do s <- get let (m', ea) = fa (snd s) modify (second (const m'))- o <- lift $ outputCode ea- pure o+ lift $ outputCode ea defaultInputCode :: Html () -> [Code] defaultInputCode h = [Append "input" (toText h)]@@ -123,33 +128,35 @@ defaultOutputCode :: (Monad m, Show a) => Either Text a -> m [Code] defaultOutputCode ea = pure $ case ea of- Left err -> [Append "debug" err]- Right a -> [Replace "output" (show a)]+ Left err -> [Append "debug" err]+ Right a -> [Replace "output" (pack $ show a)] -wrangle :: Monad m => Box m Text Text -> Box m [Code] (Text,Text)+wrangle :: Monad m => Box m Text Text -> Box m [Code] (Text, Text) wrangle (Box c e) = Box c' e' where c' = listC $ contramap code c e' = mapE (pure . either (const Nothing) Just) (parseE parserJ e) -- | {"event":{"element":"textid","value":"abcdees"}}-parserJ :: A.Parser (Text,Text)+parserJ :: A.Parser (Text, Text) parserJ = do _ <- A.string [q|{"event":{"element":"|]- e <- A.takeTill (=='"')+ e <- A.takeTill (== '"') _ <- A.string [q|","value":"|]- v <- A.takeTill (=='"')+ v <- A.takeTill (== '"') _ <- A.string [q|"}}|]- pure (e,v)+ pure (e, v) -- * code hooks+ -- * code messaging-data Code =- Replace Text Text |- Append Text Text |- Console Text |- Eval Text |- Val Text++data Code+ = Replace Text Text+ | Append Text Text+ | Console Text+ | Eval Text+ | Val Text deriving (Eq, Show, Generic, Read) code :: Code -> Text@@ -168,7 +175,7 @@ -- | replace a container and run any embedded scripts replace :: Text -> Text -> Text replace d t =- [qc|+ [qc| var $container = document.getElementById('{d}'); $container.innerHTML = '{clean t}'; runScripts($container);@@ -178,7 +185,7 @@ -- | append to a container and run any embedded scripts append :: Text -> Text -> Text append d t =- [qc|+ [qc| var $container = document.getElementById('{d}'); $container.innerHTML += '{clean t}'; runScripts($container);@@ -192,6 +199,7 @@ . Text.lines -- * initial javascript+ -- | create a web socket for event data webSocket :: RepJs webSocket =@@ -207,6 +215,7 @@ |] -- * scripts+ -- | Event hooks that may need to be reattached given dynamic content creation. refreshJsbJs :: RepJs refreshJsbJs =
− stack.yaml
@@ -1,17 +0,0 @@-resolver: nightly-2020-11-19--packages:- - '.'--extra-deps:- - numhask-0.7.0.0- - protolude-0.3.0- - random-1.2.0- - splitmix-0.1.0.3- - numhask-space-0.7.0.0- - box-0.6.2- - box-socket-0.1.2--allow-newer: true--
− test/test.hs
@@ -1,92 +0,0 @@-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Main where--import Control.Lens-import Lucid-import NumHask.Prelude-import Test.DocTest-import Test.Tasty-import Test.Tasty.Hspec-import Web.Rep-import Web.Rep.Examples-import qualified Data.Text.IO as Text--generatePage :: FilePath -> FilePath -> PageConfig -> Page -> IO ()-generatePage dir stem pc =- renderPageToFile dir (#filenames .~ concernNames "" stem $ pc) --generatePages ::- Traversable t => FilePath -> t (FilePath, PageConfig, Page) -> IO ()-generatePages dir xs =- sequenceA_ $ (\(fp, pc, p) -> generatePage dir fp pc p) <$> xs--genTest :: FilePath -> IO ()-genTest dir =- void $ generatePages dir [("default", defaultPageConfig "default", page1), ("sep", cfg2, page2)]--testVsFile :: FilePath -> FilePath -> PageConfig -> Page -> IO Bool-testVsFile dir stem pc p = do- (t,t') <- textVsFile dir stem pc p- pure (t==t')--textVsFile- :: FilePath- -> FilePath- -> PageConfig- -> Page- -> IO (Concerns Text, Concerns Text)-textVsFile dir stem pc p = do- let names = concernNames "" stem- let pc' = #filenames .~ names $ pc- let t = renderPageAsText pc' p- case pc ^. #concerns of- Inline -> do- t' <- Text.readFile (dir <> names ^. #htmlConcern)- return (t, Concerns mempty mempty t')- Separated -> do- t' <- sequenceA $ Text.readFile . (dir <>) <$> names- return (t, t')--testsRender :: IO (SpecWith ())-testsRender =- return $- describe "Web.Rep.Render" $ do- it "run genTest 'test/canned/' to refresh canned files." True- it "renderPage mempty" $- renderText (renderPage mempty) `shouldBe`- "<!DOCTYPE HTML><html lang=\"en\"><head><meta charset=\"utf-8\"></head><body><script>window.onload=function(){}</script></body></html>"- let dir = "test/canned/"- it "renderPageToFile, renderPage (compared with default canned file)" $- testVsFile dir "default" (defaultPageConfig "default") page1 `shouldReturn` True- it "the various PageConfig's" $- testVsFile dir "sep" cfg2 page2 `shouldReturn` True--testsBootstrap :: IO (SpecWith ())-testsBootstrap =- return $- describe "Web.Rep.Bootstrap" $ do- it "bootstrapPage versus canned" $- toText (renderPage bootstrapPage) `shouldBe`- "<!DOCTYPE HTML><html lang=\"en\"><head><meta charset=\"utf-8\"><link crossorigin=\"anonymous\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css\" integrity=\"sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T\" rel=\"stylesheet\"><meta charset=\"utf-8\"><meta content=\"width=device-width, initial-scale=1, shrink-to-fit=no\" name=\"viewport\"></head><body><script crossorigin=\"anonymous\" integrity=\"sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo\" src=\"https://code.jquery.com/jquery-3.3.1.slim.min.js\"></script><script crossorigin=\"anonymous\" integrity=\"sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1\" src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js\"></script><script crossorigin=\"anonymous\" integrity=\"sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM\" src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js\"></script><script>window.onload=function(){}</script></body></html>"- it "accordion versus canned" $- ( toText .- runIdentity .- flip evalStateT 0 .- accordion "acctest" Nothing $- (\x -> (pack (show x), "filler")) <$> [1..2::Int]) `shouldBe`- "<div id=\"acctest1\" class=\" accordion m-1 \"><div class=\" card \"><div id=\"acctest2\" class=\" card-header p-0 \"><h2 class=\" m-0 \"><button data-toggle=\"collapse\" data-target=\"#acctest3\" aria-controls=\"acctest3\" type=\"button\" class=\" btn btn-link collapsed \" aria-expanded=\"false\">1</button></h2></div><div data-parent=\"#acctest1\" id=\"acctest3\" aria-labelledby=\"acctest2\" class=\" collapse \"><div class=\" card-body \">filler</div></div></div><div class=\" card \"><div id=\"acctest4\" class=\" card-header p-0 \"><h2 class=\" m-0 \"><button data-toggle=\"collapse\" data-target=\"#acctest5\" aria-controls=\"acctest5\" type=\"button\" class=\" btn btn-link collapsed \" aria-expanded=\"false\">2</button></h2></div><div data-parent=\"#acctest1\" id=\"acctest5\" aria-labelledby=\"acctest4\" class=\" collapse \"><div class=\" card-body \">filler</div></div></div></div>"---- The tests-tests :: IO TestTree-tests = testGroup "the tests" <$> sequence- [ testSpec "Web.Rep.Render" =<< testsRender- , testSpec "Web.Rep.Bootstrap" =<< testsBootstrap- ]--main :: IO ()-main = do- doctest ["src/Web/Rep/SharedReps.hs"]- defaultMain =<< tests
web-rep.cabal view
@@ -1,18 +1,23 @@-cabal-version: 2.4-name: web-rep-version: 0.7.2-synopsis: representations of a web page-category: web-description: An applicative-based, shared-data representation of a web page. -bug-reports: https://github.com/tonyday567/web-page/issues-maintainer: Tony Day <tonyday567@gmail.com>-license: MIT-license-file: LICENSE.md-build-type: Simple-extra-source-files:- stack.yaml- readme.md+cabal-version: 2.4+name: web-rep+version: 0.8.0+synopsis: representations of a web page+category: web+description:+ An applicative-based, shared-data representation of a web page. +bug-reports: https://github.com/tonyday567/web-page/issues+maintainer: Tony Day <tonyday567@gmail.com>+license: MIT+license-file: LICENSE.md+build-type: Simple+extra-source-files: ChangeLog.md+tested-with: GHC ==8.8.4 || ==8.10.4 || ==9.0.1 || ==9.2.0.20210821++source-repository head+ type: git+ location: https://github.com/tonyday567/web-rep+ library exposed-modules: Web.Rep@@ -20,106 +25,55 @@ Web.Rep.Examples Web.Rep.Html Web.Rep.Html.Input- Web.Rep.Mathjax Web.Rep.Page Web.Rep.Render Web.Rep.Server Web.Rep.Shared Web.Rep.SharedReps Web.Rep.Socket- hs-source-dirs:- src- build-depends:- attoparsec >= 0.13.2 && < 0.14,- base >= 4.12 && <5,- box >= 0.6 && < 0.7,- box-socket >= 0.0.2 && < 0.2,- clay >= 0.13 && < 0.14,- concurrency >= 1.11,- generic-lens >= 1.1.0 && < 3.0,- interpolatedstring-perl6 >= 1.0.2 && < 1.1,- language-javascript >= 0.6.0 && < 0.8,- lens >= 4.17.1 && < 4.20,- lucid >= 2.9 && < 2.10,- mtl >= 2.2.2 && < 2.3,- network-simple >= 0.4.5 && < 0.4.6,- numhask >= 0.7 && < 0.8,- scotty >= 0.11.5 && < 0.13,- text >= 1.2.3 && < 1.3,- transformers >= 0.5.6 && < 0.6,- unordered-containers >= 0.2.10 && < 0.3,- wai-middleware-static >= 0.8.3,- wai-websockets >= 3.0.1.2,- websockets >= 0.12,- default-language: Haskell2010- default-extensions:- NegativeLiterals- NoImplicitPrelude- OverloadedStrings- UnicodeSyntax- ghc-options:- -Wall- -Wcompat- -Wincomplete-record-updates- -Wincomplete-uni-patterns- -Wredundant-constraints -executable rep-example- main-is: rep-example.hs- hs-source-dirs:- app+ hs-source-dirs: src build-depends:- base >= 4.12 && <5,- numhask >= 0.7 && < 0.8,- optparse-generic >= 1.3,- web-rep,- default-language: Haskell2010+ , attoparsec ^>=0.14+ , base >=4.12 && <5+ , bifunctors ^>=5.5.11+ , box ^>=0.7+ , box-socket ^>=0.2+ , clay ^>=0.13+ , concurrency ^>=1.11+ , generic-lens ^>=2.2+ , interpolatedstring-perl6 ^>=1.0+ , language-javascript >=0.6.0 && <0.8+ , lens ^>=5.0+ , lucid ^>=2.9+ , mtl ^>=2.2.2+ , scotty >=0.11.5 && <0.13+ , text ^>=1.2.3+ , transformers ^>=0.5.6+ , unordered-containers ^>=0.2.10+ , wai-middleware-static ^>=0.9+ , wai-websockets ^>=3.0.1.2+ , websockets ^>=0.12++ default-language: Haskell2010 default-extensions:- NegativeLiterals- NoImplicitPrelude- OverloadedStrings- UnicodeSyntax ghc-options:- -Wall- -Wcompat- -Wincomplete-record-updates- -Wincomplete-uni-patterns- -Wredundant-constraints- -funbox-strict-fields- -fforce-recomp- -threaded- -rtsopts- -with-rtsopts=-N+ -Wall -Wcompat -Wincomplete-record-updates+ -Wincomplete-uni-patterns -Wredundant-constraints -fwrite-ide-info+ -hiedir=.hie -test-suite test- type: exitcode-stdio-1.0- main-is: test.hs- hs-source-dirs:- test+executable web-rep-example+ main-is: rep-example.hs+ hs-source-dirs: app build-depends:- base >= 4.12 && <5,- doctest >= 0.16 && < 1.0,- lens >= 4.17 && < 4.20,- lucid >= 2.9.11 && < 2.10,- numhask >= 0.7 && < 0.8,- tasty >= 1.2.3 && < 1.3,- tasty-hspec >= 1.1.5 && < 1.2,- text >= 1.2.3 && < 1.3,- web-rep- default-language: Haskell2010+ , base >=4.12 && <5+ , optparse-generic >=1.3+ , web-rep++ default-language: Haskell2010 default-extensions:- NegativeLiterals- NoImplicitPrelude- OverloadedStrings- UnicodeSyntax ghc-options:- -Wall- -Wcompat- -Wincomplete-record-updates- -Wincomplete-uni-patterns- -Wredundant-constraints- -funbox-strict-fields- -fforce-recomp- -threaded- -rtsopts- -with-rtsopts=-N+ -Wall -Wcompat -Wincomplete-record-updates+ -Wincomplete-uni-patterns -Wredundant-constraints+ -funbox-strict-fields -fforce-recomp -threaded -rtsopts+ -with-rtsopts=-N -fwrite-ide-info -hiedir=.hie